我有以下代码:
#define RETURNS(...) -> decltype((__VA_ARGS__)) { return (__VA_ARGS__); }
template <typename This, typename... Args>
auto fun(This &&this_, Args&&... args) RETURNS(this_.fun(std::forward<Args>(args)...))
无论好坏,这让我可以互换使用fun(o,args ...)和o.f(args ...)。使用初始化列表作为参数时,难以使用它。 E.g。
fun(obj, {1, 2, 3}); // Where obj.fun eventually takes a std::vector<int>.
由于替换错误而失败,所以Clang说。请注意,obj.fun({1, 2, 3});
有效。
正如我从其他问题中理解的那样,这是因为初始化列表并不总能很好地与模板参数推断相匹配。
我最接近我想要的语法是使初始化列表更明确。为了避免冗长,我有以下内容:
template <typename T> std::initializer_list<T> il(std::initializer_list<T> &&li) { return li; }
fun(obj, il({1, 2, 3}));
有没有办法获得我想要的语法或更接近它?
我的测试程序中的Clang错误报告是:
subst.cpp:16:5: error: no matching function for call to 'fun'
fun(x, {1, 2, 3});
^~~
subst.cpp:6:6: note: candidate template ignored: substitution failure [with This
= X &, Args = <>]: too few arguments to function call, single argument 'v'
was not specified
auto fun(This &&this_, Args&&... args) RETURNS(this_.fun(std::forward<Args>(args)...))
^ ~
答案 0 :(得分:1)
您应该已提供成员函数fun
的签名。我们假设它具有以下签名:
class YourClassObj
{
public:
template<typename ...Args>
SomeType fun(Args ...args) {...}
...
};
接下来的电话:
fun(obj, {1, 2, 3});
带有转发功能的实际上导致与{1,2,3}
无法推断类型相关的非推断上下文。根据标准:
14.8.2.1从函数调用中减去模板参数[temp.deduct.call]
模板参数推导是通过将每个函数模板参数类型(称为
P
)与调用的相应参数的类型(称为A
)进行比较来完成的,如下所述。如果P
是从属类型,则从P
移除引用和简历限定符可为std::initializer_list<P'>
或P'[N]
提供P'
和N
以及参数是一个非空的初始化列表(8.5.4),然后对初始化列表的每个元素执行推导,将P'
作为函数模板参数类型,将初始化元素作为其参数,并在P'[N]
情况,如果N
是非类型模板参数,则从初始值设定项列表的长度推导出N
。 否则,初始化列表参数会将参数视为非推导上下文(14.8.2.5)。
标准中的例子:
template<class T> void f(std::initializer_list<T>); f({1,2,3}); // T deduced to int template<class T> void g(T); g({1,2,3}); // error: no argument deduced for T
std::forward
本质上是从函数调用中扣除模板类型。 Scott Meyers在他的 Effective Modern C ++ 中描述了这种情况作为perfect forwarding failure cases之一。正如他在书中所说,一个简单的解决方法是使用auto
:
auto __il = {1,2,3};
fun(obj, __il);
应编译正常,因为auto
会将{1,2,3}
推断为std::initializer_list<int>
。您可以阅读整章,以获得更详细的解释。
顺便说一下,如果原始的il
签名实际上和我假设的签名相同,那么另一个函数std::initializer_list<T>
返回fun
的方法也应该编译(测试时) GCC-4.9.1)。