通过使用显式构造函数将相同的参数传递给每个元素来构造元组

isa*_*nae 5 c++ constructor tuples explicit variadic-templates

以下内容在 Visual C++ 2015 Update 2 上运行良好。请注意,它A是不可复制的,并且A::Aexplicit.

\n\n
#include <iostream>\n#include <tuple>\n\nstruct A\n{\n    explicit A(int i)\n    {\n        std::cout << i << " ";\n    }\n\n    // non-copyable\n    A(const A&) = delete;\n    A& operator=(const A&) = delete;\n};\n\n\ntemplate <class... Ts>\nstruct B\n{\n    std::tuple<Ts...> ts;\n\n    B(int i)\n      : ts((sizeof(Ts), i)...)\n    {\n    }\n};\n\n\nint main()\n{\n    B<A, A, A, A> b(42);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

目标是将相同的参数传递给所有元组元素。它正确输出:

\n\n
42 42 42 42\n
Run Code Online (Sandbox Code Playgroud)\n\n

但是,它无法在 g++ 4.9.2 上编译。在众多消息中,tuple我认为应该调用构造函数重载:

\n\n
In instantiation of \xe2\x80\x98B<Ts>::B(int) [with Ts = {A, A, A, A}]\xe2\x80\x99:\n    33:24:   required from here\n    25:30: error: no matching function for call to\n       \xe2\x80\x98std::tuple<A, A, A, A>::tuple(int&, int&, int&, int&)\xe2\x80\x99\n          : ts((sizeof(Ts), i)...)\n\n[...]\n\n/usr/include/c++/4.9/tuple:406:19: note: template<class ... _UElements, class>\n    constexpr std::tuple< <template-parameter-1-1> >::tuple(_UElements&& ...)\n     constexpr tuple(_UElements&&... __elements)\n               ^\n/usr/include/c++/4.9/tuple:406:19: note:   template argument deduction/substitution failed:\n/usr/include/c++/4.9/tuple:402:40: error: no type named \xe2\x80\x98type\xe2\x80\x99 in\n    \xe2\x80\x98struct std::enable_if<false, void>\xe2\x80\x99\n       template<typename... _UElements, typename = typename\n
Run Code Online (Sandbox Code Playgroud)\n\n

消息中的函数签名不完整,但它指的是这个:

\n\n
template<typename... _UElements, typename = typename               \n   enable_if<__and_<is_convertible<_UElements,\n                   _Elements>...>::value>::type>                        \nexplicit constexpr tuple(_UElements&&... _elements)                      \n     : _Inherited(std::forward<_UElements>(__elements)...) { }    \n
Run Code Online (Sandbox Code Playgroud)\n\n

我的理解是is_convertible对于显式构造函数来说这是失败的。g++ 5.1 和 clang 3.5 有类似的错误消息。

\n\n

现在,在 C++14 中,20.4.2.1/10 规定:“此构造函数不应参与重载决策,除非 in 中的每个类型UTypes都可以隐式转换为其相应类型Types”。这给我的印象是 g++ 和 clang 实际上有这个权利,而 Visual C++ 过于宽容。

\n\n

[编辑:C++17 似乎已删除此限制,Visual C++ 2015 也遵循此限制。is_constructible<Ti, Ui&&>::value现在它说:“除非 [...]适用true于所有人,否则此构造函数不应参与重载决策i。” 看起来“隐式可转换”已更改为“ is_constructible”。但是,我仍然需要 C++14 解决方案。]

\n\n

我尝试从构造函数中删除explicit(我更愿意保留它)。Visual C++ 再次编译良好,但 g++ 和 clang 都抱怨删除的复制构造函数。因为int现在可以隐式转换为 an A,所以我似乎最终得到了

\n\n
explicit constexpr tuple(const Types&...)\n
Run Code Online (Sandbox Code Playgroud)\n\n

这会隐式地将ints 转换为一堆As,然后尝试复制它们。我实际上不确定如何使用其他构造函数。

\n\n

在 C++14 中,如果构造函数是 ,如何tuple通过将相同的参数传递给每个构造函数来初始化其元素explicit

\n

isa*_*nae 2

在 C++ 14 中,由于要求,当构造函数是显式时,似乎没有任何方法可以初始化元组元素is_convertible。我最终自己编写了一个简单的实现std::tuple,用于 C++14 实现,例如 Debian 8。