c ++可变参数模板构造函数和公共构造函数

时间:2015-10-05 21:01:46

标签: c++ templates constructor c++14 variadic-templates

代码如(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

如果简单构造函数与传递的参数具有完全相同的签名,则调用它。 是否有一些技巧像往常一样使用常见的构造函数与可变参数模板构造函数,没有复制类,作为参数传递,并重载构造函数,如:

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

在构造函数中没有其他标记

3 个答案:

答案 0 :(得分: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)... )
  {}
};

“首选”ctors以std::true_type为前缀,“不太偏好”的ctors以std::false_type为前缀。

这有完美转发的常见缺陷。如果您使用初始化列表,您将需要另一个“公共”ctor,例如明确地采用它。而函数名称参数魔法重载将无法正常工作。 NULLint。等

您可以想象一个版本,而不是有两个层,具有任意数字。面向公众的ctor中的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之后会遇到SQL Fiddle,因为我们已经在得到答案错误的情况下实例化了is_constructible。实际上,编译器会缓存模板实例化的结果,所以现在is_constructible的结果依赖于编译器顺序(我怀疑是ODR违规)。

答案 1 :(得分: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)

答案 2 :(得分:0)

这是另一种不使用标签分派的解决方案。这里构造函数按照选择的顺序进行测试,并且可以使用具有可变模板的构造函数,而不需要从接口variadic-templated构造函数完美转发。例如。我可以使用带有std :: pair参数的构造函数,并在其中使用brace-initializer list { "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