簡體   English   中英

C++ 中的“皮條客我的圖書館”

[英]"Pimp my Library" in C++

在 Scala 中,有一種設計模式通常稱為“pimp my library”。 基本思想是我們有一些 class Foo (大概在一些我們無法修改的庫中),我們希望Foo表現得像它有一些方法或行為frobnicate ,我們可以使用隱式 class 在之后添加方法事實。

implicit class Bar(val foo: Foo) extends AnyVal {
  def frobnicate(): Unit = {
    // Something really cool happens here ...
  }
}

然后,如果我們有一個Foo的實例,我們可以在它上面調用frobnicate ,只要Bar在 scope 中,Scala 編譯器就會足夠聰明,可以將Foo隱式轉換為Bar

val foo = new Foo()
foo.frobnicate() // Correctly calls new Bar(foo).frobnicate()

我想在 C++ 中做同樣的事情。 我知道 C++ 具有隱式強制轉換,但在訪問成員時它們似乎不會觸發。 舉個具體的例子,C++ 中的以下代碼會產生錯誤。

class Foo {}; // Assume we can't modify this class Foo

class Bar {
private:
  Foo foo;
public:
  Bar(Foo foo) : foo(foo) {}
  void frobnicate() {
    cout << "Frobnicating :)" << endl;
  }
};

void frobnicate(Bar bar) {
  cout << "Frobnicating :)" << endl;
}

int main() {
  Foo value;
  frobnicate(value);  // This works
  value.frobnicate(); // But this doesn't
  return 0;
}

value.frobnicate()行上, C++ 似乎沒有在該上下文中尋找隱式轉換。

main.cc:30:9:錯誤:“類 Foo”沒有名為“frobnicate”的成員

注意:我現在正在使用 C++11 進行編譯。 粗略地看一下較新的 C++ 版本,從那時起似乎沒有任何會影響這個問題的東西發生變化。 C++11 兼容的解決方案是理想的,但在較新的 C++ 版本中執行此操作的方法也很好,用於教學目的。

您所描述的內容稱為U nified F unction C all S yntax(UFCS),並且目前無法在C ++中完成。 任何使用D中的范圍和算法鏈接的人都可以證明這將是多么令人驚奇。

缺乏UFCS支持實際上是為什么一般的現代智慧是使用自由浮動函數和ADL用於不需要虛擬或封裝的任何東西的主要原因之一。

有提議將D的UFCS引入該語言,但它仍然只是一個提議:

https://isocpp.org/files/papers/N4165.pdf

編輯:對於想知道為什么我們想要這個功能的人,想象能夠編寫以下內容:

std::vector<int> foo(const std::vector<int>& v) {
  return v.filter([](int x) {return x > 5;})
          .map([](int x) {return x*x;})
          .sort();
}

雖然你不能完全按照你的要求去做,但你可以繼承Foo並擴展它。

class Bar : public Foo{
public:
void frobnicate() {
    cout << "Frobnicating :)" << endl;
  }
}

現在Bar擁有Foo所擁有的一切以及新方法。 http://www.cplusplus.com/doc/tutorial/inheritance/

C ++似乎沒有在該上下文中尋找隱式轉換。

那是正確的。 您正在尋找的機制在語言中不存在。

不完全是,但是...

#include <iostream>

class Foo
{
public:
    void nothingUpMySleeve()
    {
        std::cout << "Foo!\n";
    }
};

class Bar
{
private:
    Foo & foo;

public:
    Bar(Foo & foo) : foo { foo }
    {
    }

    void frobnicate()
    {
        std::cout << "Frobnicating\n";
    }

    Foo * operator ->()
    {
        return &this->foo;
    }

    operator Foo &()
    {
        return this->foo;
    }
};

void thisFunctionOnlyAcceptsFoo(Foo & foo)
{
    std::cout << "Yes, that's a Foo\n";
}

void useExtendedType(Bar wrapper)
{
    // Use the extension function
    wrapper.frobnicate();

    // Use arrow notation to use the original functions
    wrapper->nothingUpMySleeve();

    // Implicit conversion kicks in when necessary
    thisFunctionOnlyAcceptsFoo(wrapper);
}

int main()
{
    Foo value;

    // Pass to a function that uses Bar
    useExtendedType(value);
}

真的不建議在實際代碼中這樣做。 這只是一個便宜的派對把戲。

甚至不清楚Bar是否應該擁有Foo的副本或只是引用它。 我選擇僅引用是因為我假設其他一些代碼正在管理Foo的生命周期,這樣可以非常便宜地創建和銷毀Bar 您只需要注意Bar的壽命總是比Foo的壽命短,否則恐怕是鼻惡魔。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM