为什么这个可变参数模板会出错?

4

只是想知道为什么这是无效的:

#include <iostream>

template <std::size_t... Is>
void foo(Is&&... args) {
    std::cout << "foo called with " << sizeof...(Is) << "params\n"; 
}

int main() {
    foo(1, 2, 3, 4); 
}

这似乎是一个非常合理的例子,但在我手头能找到的任何编译器上都会失败

如果我将class替换为size_t,则该示例按预期工作。我还尝试使用新的auto模板参数,但没有在线编译器接受此参数,因此我不知道这是无效用例还是符合性问题。


4
"is" 不是一种类型,而是 size_t 类型的一个值。 - NathanOliver
1
你永远不会写void foo(1 a, 2 b, 3 c, ...),那么为什么要写void foo(Is... )呢? ;) - Holt
3
@BaummitAugen所指的重复目标似乎不太正确。 - NathanOliver
@NathanOliver 为什么?你无法推断非类型模板参数,无论是否是可变参数都没有区别。 - Baum mit Augen
@BaummitAugen 这里的问题不在于推导,而在于 OP 正试图使用非类型模板参数来指定参数的类型...我不知道你怎么称呼它,但这与非类型模板参数推导无关。 - Holt
1
@BaummitAugen 因为OP甚至没有尝试推导值。他对类型和非类型模板之间的区别感到困惑。我们甚至没有进行推导,因为void foo(Is&&... args)是一个明显的编译器错误。 - NathanOliver
2个回答

10

这不是有效的C++代码,这就是原因。

如果您使用模板参数1, 2, 3, 4实例化该函数模板,那么在将参数替换为模板后,您将得到以下签名:

void foo(1&&, 2&&, 3&&, 4&&);

这显然不是一个有效的函数。

如果你想编写一个函数模板,它可以接受任意数量的参数,但只有当它们是正确类型时才能接受,你可以在C++17中像这样实现:

template<typename T>
  using is_size_t = std::is_same<T, std::size_t>;

template<typename... T>
  std::enable_if_t<std::conjunction<is_size_t<T>...>::value>>
  foo(T&&... args);

或者,另一种方法(同样使用C++17):
template<typename... T>
  std::enable_if_t<(std::is_same_v<std::size_t, T> && ...)>
  foo(T&&... args);

对于C++14,您需要自己实现std::conjunction,例如使用p0032r1中的and_模板。


我认为C++14没有折叠表达式吗? - Barry
1
@Barry,糟糕,我忘记了C++14有多原始;) - Jonathan Wakely
1
@JonathanWakely 它不是原始的,它是完整的。 - skypjack

2

最近我一直在使用另一种语言,完全像Monika一样。为了补充Jonathan的答案(感谢解释和评论),以下是如何确保所有参数都是size_t类型(这正是我想要做的)使用概念

template <class... Is>
    requires (std::is_same<Is, int>::value && ...)
void foo(Is&&... args) { /*...*/ }

甚至可以通过定义专门的概念来实现(我的最爱)。
template <class T> concept bool Integer = std::is_same<T, int>::value; 

template <Integer... Is> void foo(Is&&... args) { /*...*/ }
//        ^^^^^^^awesome

Live


概念不是C++语言的一部分。 - Sjoerd
1
@Sjoerd 我并不是在说它们是这样的,对吧?推迟它们的添加之一的原因是让C++社区进行实验、测试和详细阐述。 - Lorah Attkins

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接