使用带有Variadic模板的初始化列表

时间:2014-02-05 09:53:30

标签: c++ c++11 variadic-templates initializer-list type-deduction

我有以下代码:

#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)...))
     ^                                                                              ~

1 个答案:

答案 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)。