2015-10-05 75 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