创建对象的可变参数模板方法

variadic template method to create object

我在模板 class(T_ 类型)中有一个可变参数模板方法,看起来像这样

template < typename T_ >
class MyContainer {
  public:
  
  ...
  template <typename ...A>
  ulong add (A &&...args) 
  {
    T_ t{args...};
    // other stuff ...
    vec.push_back(t);
    // returning an ulong 
  }
}

所以基本上我试图让这个 class 适应任何类型 T_ 但是因为我无法提前知道它的构造函数需要哪些类型,所以我使用了可变参数。我从 stl 库的 emplace_back 方法中获得灵感。

尽管如此,如果我尝试执行类似的操作,我会收到一条关于缩小整数类型转换的警告

MyContainer<SomeClassRequiringAnUlong> mc;
mc.add(2);
warning: narrowing conversion of ‘args#0’ from ‘int’ to ‘long unsigned int’ [-Wnarrowing]

所以我想知道我是否可以做些什么。有没有办法根据模板参数T_(创建对象时已知)告诉方法应该采用哪些参数类型?

一般来说,不会。 C++ 的规则明确允许进行隐式转换。 C++ 的作者使其中一些转换可能不安全的事实是另一回事。

如果用户输入错误的参数,您可以将 std::is_constructible<T,A&&...> static_assert 或 SFINAE 添加到代码中,使编译器错误不那么难看,但它不会解决隐式转换。

从设计的角度来看,代码不应该关心这个,emplace_XXX 的目的是允许 T{args...}.

允许的调用

注意:您很可能想转发 T element{std::forward<A>(args)...}; 之类的参数,并将元素移动到向量 vec.push_back(std::move(t));.

也就是说,代码

T_ t{args...};
//...
vec.push_back(t);

emplace 函数的作用完全相反,它们的目的是在其最终目的地就地创建元素。不要将其复制或移动到那里。

Is there any way to tell the method which parameters' type it is supposed to take according to the template parameter T_ (which is known when the object is created)?

在你的情况下,你应该使用直接初始化(())而不是列表初始化({})(以避免不必要的缩小检查)。

考虑 Tvector<int>:

的情况
MyContainer<std::vector<int>> mc;
mc.add(3, 0);

您希望 mc.add(3, 0) 做什么?在您的 add() 函数中,T_ t{args...} 将调用 vector<int>{3,0} 并创建大小为 2 的 vector,这显然是错误的。您应该使用 T_ t(args...) 调用 vector(size_type count, const T& value) 的重载来构建大小为 3 的向量,就像 emplace_back() 那样。

值得注意的是,由于P0960R3,如果T_是聚合,T_ t(std::forward<Args>(args)...)也可以进行聚合初始化。

你看错方向了。需要修复的不是模板,而是用户代码。要看问题,首先要明白2signed int类型。所以你试图用 signed int 构造一个对象,而构造函数期望 long unsigned int 。然后可以如下编写该问题的最小重现。

class SomeClassRequiringAnUlong {
public:
    SomeClassRequiringAnUlong(unsigned long) {}
};

int main() {
  int v = 2;
  SomeClassRequiringAnUlong obj{v};
}

警告只是指出,这种从 signed intlong unsigned int 的缩小转换具有潜在风险,可能会让您措手不及。例如,

int v = -1;
SomeClassRequiringAnUlong obj{v};

仍然编译运行,但结果可能不是你想要的。现在您看到问题出在用户代码上。我看到两种修复方法。 1) 从一开始就使用构造函数期望的类型。在您的情况下,这会将 mc.add(2) 更改为 mc.add(2ul)2ullong unsigned int 类型。 2) 显式进行类型转换,以通知编译器缩小转换是设计使然,并且没有问题。那就是将 mc.add(2) 更改为 mc.add(static_cast<long unsigned int>(2)).

请注意,正如其他答案所指出的那样,您的模板中存在问题(尽管与警告不太相关)。但它们与您提出的具体问题无关。所以我不再详细介绍它们。