模板模板参数的模板参数推导错误

Deduction error of template parameters of a template template argument

我在编写非模板的模板成员时遇到问题 class,特别是在推导模板模板参数的模板参数时。

以下代码(最小)说明了我的问题。我怀疑那里至少有两个不同的问题。

#include <iostream>
#include <list>

using namespace std;

struct A
{
    list<int> l;

    template<template<class> class Iterable> 
    A(Iterable<int>& it);
};

template<template<class> class Iterable>
A::A(Iterable<int>& it) : l(list<int>())
{
    for(int i : it)
        l.push_back(i);
}

int main()
{
    list<int> l = {1, 2, 3, 4};
    A a(l);
    for(int i : a.l)
        cout << i << " ";
     cout << endl;

    A b = {1, 2, 3, 4};
    for(int i : b.l)
        cout << i << " ";
    cout << endl;
}

备注:我真的很想提供 class 的定义。我还希望 A 的构造函数的原型能够处理整数列表、整数向量以及初始化列表。

I suspect there are at least two different problems down there.

正确:我看到三个不同的问题。

1) std::list需要两个模板参数;第二个是默认的(std::allocator<T>,其中 T 是第一个模板参数);以及向量、双端队列、集合等需要更多的模板参数(有些是默认的)。

因此 template<template<class> class>std::list 和其他 STL 容器不匹配。

为了更通用,我建议如下

template <template <typename...> class Iterable, typename ... Ts> 
A (Iterable<int, Ts...> & it);

2) A b = {1, 2, 3, 4}; 不起作用,因为您正在调用带有四个参数的构造函数,而您只有一个带有一个参数的构造函数。所以你必须用

来明确容器
A b = std::list<int>{1, 2, 3, 4};

鉴于您希望 std::initializer_list 作为默认容器,如果您接受将图形加倍({ { 1, 2, 3, 4 } },而不是 { 1, 2, 3, 4 },以将单个参数传递给构造函数),您可以指示 std::initializer_list(或其他容器,如果您愿意)作为默认值 Iterable

我的意思是...如果你声明构造函数如下

   template <template <typename...> class Iterable = std::initializer_list,
             typename ... Ts> 
   A (Iterable<int, Ts...> const & it);

那么你可以将b初始化为

   A b {{1, 2, 3, 4}};

3) 签名Iterable<int, Ts...> & it,在你的模板构造函数中,接受一个左值引用,所以接受

list<int> l = {1, 2, 3, 4};
A a(l);

但不接受

A b = std::list<int>{1, 2, 3, 4};

因为 std::list<int>{1, 2, 3, 4} 是一个 r 值。

要解决这个问题,您可以为 r 值引用编写一个不同的构造函数,但在这种情况下,我怀疑这是可以接受的,只需修改模板构造使其能够接受 const 左值引用

A (Iterable<int, Ts...> const & it); 
// .....................^^^^^

奖金(题外话)建议:如果您有兴趣接受 STL 容器,您可以使用接受几个迭代器的 l 构造函数来简化模板构造函数(begin()end()), 所以

template <template <typename...> class Iterable, typename ... Ts>
A::A (Iterable<int, Ts...> const & it) : l{it.cbegin(), it.cend()}
 { }

template <template <typename...> class Iterable, typename ... Ts>
A::A (Iterable<int, Ts...> const & it) : l{std::cbegin(it), std::cend(it)}
 { }

以下是您修改后的代码

#include <initializer_list>
#include <iostream>
#include <list>

struct A
 {
   std::list<int> l;

   template <template <typename...> class Iterable = std::initializer_list,
             typename ... Ts> 
   A (Iterable<int, Ts...> const & it);
 };

template <template <typename...> class Iterable, typename ... Ts>
A::A (Iterable<int, Ts...> const & it) : l{std::cbegin(it), std::cend(it)}
 { }

int main ()
 {
   std::list<int> l {1, 2, 3, 4};
   A a(l);

   for (auto i : a.l)
      std::cout << i << " ";
   std::cout << std::endl;

   A b {{1, 2, 3, 4}};

   for (auto i : b.l)
      std::cout << i << " ";
   std::cout << std::endl;
 }