2015-10-05 74 views
8

類似的代碼(C++ 14):如果它有確切相同的簽名傳遞的參數C++可變參數模板的構造,共同構造

struct S { int a; int b; }; 

class C 
{ 
    public: 
    C(char const*, size_t) {} // 1 
    C(S const&) {} // 2 
    C(S const*) {} // 3 
    template<typename ...T> C(T&& ...) {} // 4 

// C(S) {} // 5 
// C(S*) {} // 6 
}; 

S s { 1, 2 }; 
C c1 { s }; // calls 4 and not 2 
C c2 { "abc", 3 }; // calls 4 and not 1 
C c3 { (char const*)"abc", (size_t)3 }; // calls 1 - ok 
C c4 { s }; // calls 5 if uncommented 
C c5 { &s }; // calls 6 if uncommented 
S const s2 {}; 
C c6 { &s2 }; // calls 3 

簡單的構造函數被調用。 有一些技巧,使用普通的構造像往常一樣用可變參數模板的構造,無需複製類,作爲參數傳遞,和重載的構造函數,如:在構造函數中

C(S const*) {} 
C(S*) {} 

而且沒有額外的標籤

+0

您希望保持可變模板和非模板構造函數,並確保它首先調用非模板構造函數;它是否正確? –

+0

@ Lorenzo Belli是的 –

+0

U呢?它不被使用,這是故意的嗎? –

回答

6

創建兩層構造函數。然後標籤發送。

template<template<class...>class Z, class T> 
struct is_template:std::false_type{}; 
template<template<class...>class Z, class...Ts> 
struct is_template<Z, Z<Ts...>>:std::true_type{}; 

struct foo { 
private: 
    template<class T> struct tag{ explicit tag(int) {} }; 
public: 
    foo(tag<std::true_type>, const char*, size_t); 
    template<class...Ts> 
    foo(tag<std::false_type>, Ts&&...ts); 

public: 
    foo() = default; // or whatever 
    template<class T0, class...Ts, 
    std::enable_if_t<!is_template<tag, std::decay_t<T0>>{},int> =0> 
    foo(T0&&t0, Ts&&...ts): 
    foo(tag<typename std::is_constructible< foo, tag<std::true_type>, T0&&, Ts&&... >::type>{0}, std::forward<T0>(t0), std::forward<Ts>(ts)...) 
    {} 
}; 

的「優選的」構建函數與std::true_type前綴中,「較不優選的」構建函數的前綴爲std::false_type

這有完美的轉發通常的缺陷。例如,如果您使用初始化程序列表,那麼您將需要另一個「公開」ctor,例如,明確採用該列表。而函數名稱參數的魔法重載將無法工作。 NULLint。等

你可以想象一個版本,而不是有兩層,有一個任意數字。在面向公衆的is_constructible<...>子句中,取而代之的是一些可以找到最高N的魔術,例如tag<N>, blah...可以構建對象(或者最低的N,無論您想要做什麼)。然後它返回類型tag<N>,然後分派到該層。

使用的技術是這樣的:

template <typename... T, 
     typename = std::enable_if_t<!std::is_constructible<C, T&&...>::value> 
     > 
C(T&&...) { } 

跑進a serious problem的道路,因爲我們在它得到的答案錯誤上下文已實例化is_constructible。實際上,編譯器會緩存模板實例的結果,所以現在is_constructible的結果是依賴於編譯器順序的(我懷疑ODR違例)。

+0

*「我懷疑ODR違規」*我認爲這是[temp.point] p8。 – dyp

+1

不應'is_constructible'採取''?這太棒了,順便說一句。 – Barry

+1

'is_constructible'應該在不相關的上下文中執行訪問檢查(即,沒有私有文件)。 –

4

您可以啓用您的可變參數構造函數當且僅當這些參數不允許您使用std::is_constructible以某種其他方式構造C

即:

template <typename... T, 
      typename = std::enable_if_t<!std::is_constructible<C, T&&...>::value> 
      > 
C(T&&...) { } 

隨着這種變化,C c1{s}也叫(2)C c2{"abc", 3}也叫(1),而C c7{1, 2, 3, 4, 5}將調用(4)

+4

我有點困惑:爲什麼這不會導致無限遞歸模板實例化? – dyp

+1

@dyp我相信這個構造函數直到聲明符的末尾纔會被考慮。 – Barry

+0

但是,這是一個默認的模板參數,它不應該稍後實例化嗎? – dyp

0

這是另一個不使用標籤分派的解決方案。這裏的構造函數按照選定的順序進行測試,並且可以使用簡單的構造函數和可變參數模板構造函數,但沒有從界面可變模板構造函數完美轉發。例如。我可以使用std :: pair參數的構造函數,並在其中使用括號初始值設定項列表{ "String", 6 },而不是明確地通過std::pair<char const*, size_t>("String", 6)等等,如示例所示。該方法不僅可以用來控制構造函數的優先級,還可以用來控制其他成員的重載優先級。目前它需要一個幫手類。

我是新的不同的元編程技巧,它只是一個建議。我不會改進它。

#include <type_traits> 
#include <cstddef> 
#include <iostream> 
using namespace std; 

一些標準樣類:

struct null_helper { static constexpr int const value = 0; }; 

template<template<unsigned> typename Helper, unsigned order, typename ...TT> 
class constructible_from_order 
{ 
    using PrevType = typename conditional<(order < 1), null_helper, 
              constructible_from_order<Helper, order-1, TT&&...>>::type; 
    static constexpr int const prev = PrevType::value; 
    static constexpr bool const is_this_constructible = is_constructible<Helper<order>, TT&&...>::value; 
    public: 
    static constexpr int const value = prev ? prev : is_this_constructible ? order : 0; 

}; // template class constructible_from_order 

template<template<unsigned> typename Helper, unsigned order, typename ...TT> 
using enable_in_order = enable_if<(constructible_from_order<Helper, order, TT&&...>::value == order)>; 

template<template<unsigned> typename Helper, unsigned order, typename ...TT> 
using enable_in_order_t = typename enable_in_order<Helper, order, TT&&...>::type; 

例如類定義:

using blob_data = pair<char const*, size_t>; 

class C { 

    template<unsigned order> 
    class helper 
    { 
     public: 
     helper(char const*, size_t) {} // 1 

     helper(blob_data const&, blob_data const&) {} // 1 

     template<typename T, typename = enable_if_t<(order == 2) && sizeof(T)>> 
     helper(blob_data const&, T&&) {} // 2 

     template<typename T, typename = enable_if_t<(order == 3) && sizeof(T)>> 
     helper(T&&, blob_data const&) {} // 3 

     template <class... Ts, typename = enable_if_t<(order == 4) && sizeof...(Ts)>> 
     helper(Ts&&...) {} // 4 

    }; // template class helper 

    public: // constructors: 

    // order 1 
    C(char const*, size_t) { cout << "1" << endl; } 

    // order 1 
    C(blob_data const&, blob_data const&) { cout << "1" << endl; } 

    // order 2 
    template<typename T, typename = enable_in_order_t<helper, 2, blob_data const&, T&&>> 
    C(blob_data const&, T&&) { cout << "2" << endl; } 

    // order 3 
    template<typename T, typename = enable_in_order_t<helper, 3, T&&, blob_data const&>> 
    C(T&&, blob_data const&) { cout << "3" << endl; } 

    // order 4 
    template <class... Ts, typename = enable_in_order_t<helper, 4, Ts&&... >> 
    C(Ts&&...) { cout << "4" << endl;} 

    public: // member functions: 

    // order 1 
    void fun(char const*, size_t) { cout << "1" << endl; } 

    // order 1 
    void fun(blob_data const&, blob_data const&) { cout << "1" << endl; } 

    // order 2 
    template<typename T, typename = enable_in_order_t<helper, 2, blob_data const&, T&&>> 
    void fun(blob_data const&, T&&) { cout << "2" << endl; } 

    // order 3 
    template<typename T, typename = enable_in_order_t<helper, 3, T&&, blob_data const&>> 
    void fun(T&&, blob_data const&) { cout << "3" << endl; } 

    // order 4 
    template <class... Ts, typename = enable_in_order_t<helper, 4, Ts&&... >> 
    void fun(Ts&&...) { cout << "4" << endl;} 

}; // class C 

用作:

int main() { 

    char const* str = "aaa"; 

    // constructors: 
    cout << "Constructors: " << endl; 
    cout << "1: "; C c1 {  str, size_t{5} }; 
    cout << "1: "; C cx { { str, 5 }, { str, 5 } }; 
    cout << "2: "; C c2 { { str, 5 },  str }; 
    cout << "3: "; C c3 {  str, { str, 5 } }; 
    cout << "4: "; C c4 {  str,  str }; 
    cout << endl; 

    // functions: 
    cout << "Functions: " << endl; 
    cout << "1: "; c1.fun(  str, size_t{5}); 
    cout << "1: "; c1.fun({ str, 5 }, { str, 5 }); 
    cout << "2: "; c1.fun({ str, 5 },  str); 
    cout << "3: "; c1.fun(  str, { str, 5 }); 
    cout << "4: "; c1.fun(  str,  str); 
    cout << endl; 

} // main 

程序輸出:

Constructors: 
1: 1 
1: 1 
2: 2 
3: 3 
4: 4 

Functions: 
1: 1 
1: 1 
2: 2 
3: 3 
4: 4