2013-07-30 24 views
3

我試圖編寫一個(C++ 98)程序,其中發生以下模式:我有一個非常簡單的通用元組類,並且需要用值填充它使用工廠建造。小例子,代碼如下:使用工廠填充元組並避免複製

#include <iostream> 

class diagnostics 
{ 
private: 
    int payload; 
public: 
    diagnostics(int a) 
    : payload(a) 
    { 
    std::cout << "constructor\n"; 
    } 

    diagnostics(const diagnostics& o) 
    : payload(o.payload) 
    { 
    std::cout << "copy constructor\n"; 
    } 

    ~diagnostics() 
    { 
    std::cout << "destructor [" << payload << "]\n"; 
    } 
}; 

struct creator 
{ 
    static diagnostics create() 
    { 
    static int i = 0; 
    return diagnostics(++i); 
    } 
}; 

template<class Head, class Tail> 
struct tuple 
{ 
    Head head; 
    Tail tail; 
    typedef Head head_t; 
    typedef Tail tail_t; 

    tuple(const Head& h, const Tail& t) 
    : head(h), tail(t) 
    { 
    } 
}; 
struct empty_tuple { }; 

template<class Tuple, class Create> 
struct create_helper 
{ 
    static Tuple create() 
    { 
    return Tuple(Create::create(), 
     create_helper<typename Tuple::tail_t, Create>::create()); 
    } 
}; 
template<class Create> 
struct create_helper<empty_tuple, Create> 
{ 
    static empty_tuple create() 
    { 
    return empty_tuple(); 
    } 
}; 
template<class Tuple, class Create> 
Tuple create() 
{ 
    //return Tuple(Create::create(), empty_tuple()); //(*) 
    return create_helper<Tuple, Create>::create(); 
} 

int main() 
{ 
    typedef tuple<diagnostics, empty_tuple> tuple_t; 
    tuple_t a = create<tuple_t, creator>(); 
} 

輸出是

constructor 
copy constructor 
destructor [1] 
destructor [1] 

我想擺脫中間兩行。

爲了簡化討論,我們可以在上面的代碼中取消標註(*)行的註釋;這將打破通用性而不是程序。

現在我的主要問題:我該如何解決這種情況?是否有標準防止RVO(推測RVO將不得不在這裏遞歸地完成)?如果不是,接受編譯器不夠好,有沒有一種方法可以使它以明確的方式發生?我可以將create()的callside複雜化,但我不想讓元組中的對象複雜化(特別是其中一些不能默認構造,我不想引入額外的「單元化」向他們說明)。可能安置新的可能的幫助?

以下問題似乎有關,但最終沒有幫助:Why does not RVO happen here?

+1

我認爲這行'return create_helper :: create();'在輸出中創建了額外的兩行。該行正在返回一個已創建的對象,然後'tuple_t a = create ();'然後使用複製構造函數複製到'a',然後破壞'create <...>()'的返回值。 。 – abiessu

+1

編譯器認爲您的診斷類具有不平凡的構造函數/析構函數(具有不小的副作用)並因此拒絕RVO似乎不是不合理的。請注意,該標準特別允許在複製初始化的情況下進行優化('diagnostics instance = diagnostics(...)')IIRC – sehe

+0

最小的示例不會生成顯示的輸出,因爲沒有人調用'creator'。請添加一個'main'測試工具,它將實際生成顯示的輸出(理想情況下爲高優化級別)。 – Yakk

回答

2

我覺得有點傻五分鐘後回答我的問題,但這似乎是最合適的方式來獲得以下信息跨:

這是一個解決方案。我們可以將創建代碼到元組類本身:

struct FILLIT { }; 

template<class Head, class Tail> 
struct tuple 
{ 
    Head head; 
    Tail tail; 
    typedef Head head_t; 
    typedef Tail tail_t; 

    tuple(const Head& h, const Tail& t) 
    : head(h), tail(t) 
    { 
    } 

    tuple(const tuple& o) 
    : head(o.head), tail(o.tail) 
    { 
    std::cout << "tuple copy\n"; 
    } 

    template<class Create> 
    tuple(FILLIT, Create c) 
     : head(Create::create()), tail(FILLIT(), c) 
    { 
    } 
}; 
struct empty_tuple 
{ 
    empty_tuple() {}; 
    template<class C> 
    empty_tuple(FILLIT, C) {}; 
}; 

兼容的實現創造的是

template<class Tuple, class Create> 
Tuple create() 
{ 
    return Tuple(FILLIT(), Create()); 
} 

我仍然有興趣在其他方法。