C++可变参数模板构造函数和通用构造函数

c++ variadic template constructor and common constructors

代码如 (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*) {}

并且在构造函数中没有额外的标签

当且仅当这些参数不允许您使用 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)

创建两层构造函数。然后标记dispatch.

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)... )
  {}
};

"preferred" 个 ctors 以 std::true_type 为前缀,"less preferred" 个 ctors 以 std::false_type.

为前缀

这具有完美转发的常见缺陷。例如,如果您采用初始化列表,您将希望有另一个 "public" 构造函数来明确采用它。并且函数名称参数神奇的重载将不起作用。 NULL 是一个 int。等等

您可以想象一个版本,它没有两层,而是具有任意数量。 public 面向 ctor 中的 is_constructible< ... > 子句被替换为一些找到最高 N 的魔法,使得 tag<N>, blah... 可以构造对象(或者,最低的 N,无论哪种方式你想做)。然后它 returns 类型 tag<N>,然后分派到该层。

使用这样的技术:

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

在路上遇到 ,因为我们在 得到错误答案 的上下文中实例化了 is_constructible。实际上,编译器会缓存模板实例化的结果,所以现在 is_constructible 的结果取决于编译器顺序(我怀疑是 ODR 违规)。

这是另一种不使用标签分发的解决方案。这里的构造函数按照选定的顺序进行测试,并且可以使用带有可变参数模板的简单构造函数,而无需从接口可变参数模板构造函数进行完美转发。例如。我可以使用带有 std::pair 参数的构造函数,并在其中使用大括号初始化列表 { "String", 6 } ,而不是显式传递 std::pair<char const*, size_t>("String", 6) 等等,如示例所示。该方法不仅可以用来控制构造函数的优先级,还可以用来控制其他成员重载的优先级。目前需要帮手class。

我是不同元编程技巧的新手,这只是一个提议。我不会改进它。

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

一些类似标准的 classes:

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;

Class 定义例如:

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