Ром*_*тев 8 c++ templates constructor variadic-templates c++14
代码如(c ++ 14):
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
Run Code Online (Sandbox Code Playgroud)
如果简单构造函数与传递的参数具有完全相同的签名,则调用它.是否有一些技巧像往常一样使用常见的构造函数与可变参数模板构造函数,没有复制类,作为参数传递,并重载构造函数,如:
C(S const*) {}
C(S*) {}
Run Code Online (Sandbox Code Playgroud)
并且在构造函数中没有其他标记
创建两层构造函数.然后标记调度.
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)... )
{}
};
Run Code Online (Sandbox Code Playgroud)
"优选的"ctors是前缀std::true_type,"不太优选"的ctors是预先加上的std::false_type.
这通常有完美转发的缺陷.如果您使用初始化列表,您将需要另一个"公共"ctor,例如明确地采用它.而函数名称参数魔法重载将无法正常工作. NULL是一个int.等等.
您可以想象一个版本,而不是有两个层,具有任意数字.is_constructible< ... >公共面对ctor 的条款被替换为一些魔法,它找到最高N,这样tag<N>, blah...可以构造对象(或者,最低N,无论你想做什么).然后它返回类型tag<N>,然后调度到该层.
使用这样的技术:
template <typename... T,
typename = std::enable_if_t<!std::is_constructible<C, T&&...>::value>
>
C(T&&... ) { }
Run Code Online (Sandbox Code Playgroud)
遇到了严重的问题,因为我们已经is_constructible在一个上下文中得到答案错误的实例化.实际上,编译器会缓存模板实例化的结果,所以现在结果is_constructible是编译器顺序依赖(我怀疑是ODR违规).