兼具聚合初始化和模板推导

Ant*_*rez 1 c++ templates aggregate-initialization type-deduction c++17

背景:

\n

C++17 有两大特性:聚合初始化模板类型推导(对于类)。聚合初始化允许您实例化字段而无需复制或移动它们,并且模板类型推导使您不必指定参数的类型。

\n

下面代码中的类Wrapper就是一个例子。只要HAVE_MOVE_AND_COPY未定义,它就具有聚合初始化,但模板类型推导不起作用。

\n

另一方面,如果定义HAVE_MOVE_AND_COPY ,则模板类型推导有效,但聚合初始化会中断。我怎样才能两者兼得?

\n
#include <cstdio>\n#include <utility>\n\ntemplate<class T> \nstruct Wrapper {\n    T value;\n   #ifdef HAVE_MOVE_AND_COPY\n    Wrapper(T const & val) : value{val} {}\n    Wrapper(T && val) : value{std::move(val)} {}\n   #endif\n    Wrapper(Wrapper const &) = default;\n    Wrapper(Wrapper &&) = default;\n    \n\n};\n\nstruct VocalClass {\n    VocalClass() { puts("VocalClass()"); }\n    VocalClass(VocalClass const&) { puts("VocalClass(VocalClass const &)"); }\n    VocalClass(VocalClass &&) { puts("VocalClass(VocalClass &&)"); }\n};\n\nint main() {\n    Wrapper<VocalClass> w { VocalClass() }; \n\n   #ifdef TRY_DEDUCTION\n    Wrapper w2 { VocalClass() }; \n   #endif\n}\n
Run Code Online (Sandbox Code Playgroud)\n

例子:

\n

没有发生移动或复制,但你没有模板推导:

\n
$ c++ -std=c++17 example.cc && ./a.out\nVocalClass()\n
Run Code Online (Sandbox Code Playgroud)\n

有模板推导,但VocalClass被移动:

\n
$ c++ -DHAVE_MOVE_AND_COPY -DTRY_DEDUCTION -std=c++17 example.cc && ./a.out \nVocalClass()\nVocalClass(VocalClass &&)\nVocalClass()\nVocalClass(VocalClass &&)\n
Run Code Online (Sandbox Code Playgroud)\n

如果没有HAVE_MOVE_AND_COPY,模板类型推导就会中断:

\n
sky@sunrise:~$ c++ -DTRY_DEDUCTION -std=c++17 example.cc && ./a.out \nexample.cc: In function \xe2\x80\x98int main()\xe2\x80\x99:\nexample.cc:27:31: error: class template argument deduction failed:\n     Wrapper w2 { VocalClass() };\n                               ^\nexample.cc:27:31: error: no matching function for call to \xe2\x80\x98Wrapper(VocalClass)\xe2\x80\x99\nexample.cc:12:5: note: candidate: \xe2\x80\x98template<class T> Wrapper(Wrapper<T>&&)-> Wrapper<T>\xe2\x80\x99\n     Wrapper(Wrapper &&) = default;\n     ^~~~~~~\nexample.cc:12:5: note:   template argument deduction/substitution failed:\nexample.cc:27:31: note:   \xe2\x80\x98VocalClass\xe2\x80\x99 is not derived from \xe2\x80\x98Wrapper<T>\xe2\x80\x99\n     Wrapper w2 { VocalClass() };\n                               ^\nexample.cc:11:5: note: candidate: \xe2\x80\x98template<class T> Wrapper(const Wrapper<T>&)-> Wrapper<T>\xe2\x80\x99\n     Wrapper(Wrapper const &) = default;\n     ^~~~~~~\nexample.cc:11:5: note:   template argument deduction/substitution failed:\nexample.cc:27:31: note:   \xe2\x80\x98VocalClass\xe2\x80\x99 is not derived from \xe2\x80\x98const Wrapper<T>\xe2\x80\x99\n     Wrapper w2 { VocalClass() };\n                               ^\nexample.cc:5:8: note: candidate: \xe2\x80\x98template<class T> Wrapper(Wrapper<T>)-> Wrapper<T>\xe2\x80\x99\n struct Wrapper {\n        ^~~~~~~\nexample.cc:5:8: note:   template argument deduction/substitution failed:\nexample.cc:27:31: note:   \xe2\x80\x98VocalClass\xe2\x80\x99 is not derived from \xe2\x80\x98Wrapper<T>\xe2\x80\x99\n     Wrapper w2 { VocalClass() };\n                  \n
Run Code Online (Sandbox Code Playgroud)\n

问题

\n

有什么方法可以同时进行模板类型推导和聚合初始化吗?

\n

Bar*_*rry 5

首先,术语是“类模板参数推导”。

其次,你需要的是一个推演指南

template<class T> 
struct Wrapper {
    T value;
};

template <typename T>
Wrapper(T) -> Wrapper<T>; // this is a deduction guide
Run Code Online (Sandbox Code Playgroud)

如果没有构造函数,则需要其他方法来指导推导。这就是它的用途,它允许:

Wrapper w{4}; // ok, Wrapper<int>
Run Code Online (Sandbox Code Playgroud)