15

非类型模板参数显然不是类型,例如:

template<int x>
void foo() { cout << x; }

除了这种情况,还有其他选择int,我想参考这个很好的答案

现在,有一件事情让我很烦恼:结构。考虑:

struct Triple { int x, y, z; };

Triple t { 1, 2, 3 };

template<Triple const& t>
class Foo { };

现在,使用正常的非类型引用语义,我们可以编写:

Foo<t> f;

这里值得注意的是t can't beconstexpr或 even const,因为这意味着内部链接,这基本上意味着该行不会编译。我们可以通过声明tas来绕过它const extern。这本身可能有点奇怪,但真正让我想知道的是为什么这是不可能的:

Foo<Triple { 1, 2, 3 }> f;

我们从编译器中得到了一个非常不错的错误:

错误:Triple{1, 2, 3}不是类型的有效模板参数,const Triple&因为它不是左值。

我们不能Triple在模板中按值指定,因为这是不允许的。但是,我无法理解那一小行代码的真正问题。不允许使用结构作为值参数的原因是什么。如果我可以使用三个ints,为什么不使用三个整数的结构?如果它只有微不足道的特殊成员,那么它在处理上应该与只有三个变量没有什么不同。

4

3 回答 3

19

更新了用户的答案:

C++20 adds support for class literal (class with constexpr constructor) non-type template parameters, which would allow the example in the original question to work, provided the template parameter is accepted by value:

template<Triple t> // Note: accepts t by value
class Foo { };

// Works with unnamed instantiation of Triple.
Foo<Triple { 1, 2, 3 }> f1 {};

// Also works if provided from a constexpr variable.
constexpr Triple t { 1, 2, 3 };
Foo<t> f2 {};

Further, all template parameter instances of Triple { 1, 2, 3 } throughout the program will refer to the same static storage duration object:

template<Triple t1, Triple t2>
void Func() {
    assert(&t1 == &t2); // Passes.
}

constexpr Triple t { 1, 2, 3 };

int main()
{
    Func<t, Triple {1, 2, 3}>();
}

From cppreference:

命名类类型 T 的非类型模板形参的标识符表示类型为 const T 的静态存储持续时间对象,称为模板形参对象,其值是对应模板实参转换为模板参数。相同类型的程序中所有具有相同值的模板参数表示相同的模板参数对象。

请注意,模板参数允许的类文字类型有很多限制。有关更多详细信息,请查看我写的这篇博客文章,解释文字类 NTTP 的用法和限制:文字类作为 C++20 中的非类型模板参数

于 2020-02-16T00:25:19.913 回答
14

让这一点工作很容易,但是人们会抱怨使用 struct 模板参数在其他模板参数所做的所有相同情况下都不起作用(考虑部分特化,或如何处理operator==)。

在我看来,整块蛋糕太乱了,只吃一小块还不够令人满意,可能更令人沮丧。只做这一点点工作不会给我比下面这样的东西更多的力量,它具有开箱即用的各种东西(包括部分专业化)的额外优势。

template <int X, int Y, int Z>
struct meta_triple {
    // static value getters
    static constexpr auto x = X;
    static constexpr auto y = Y;
    static constexpr auto z = Z;
    // implicit conversion to Triple 
    constexpr operator Triple() const { return { X, Y, Z }; }
    // function call operator so one can force the conversion to Triple with
    // meta_triple<1,2,3>()()
    constexpr Triple operator()() const { return *this; }
};
于 2013-04-09T08:26:49.093 回答
7

您可以定义tconst extern,给它外部链接。然后构造工作:

struct Triple { int x, y, z; };

const extern Triple t { 1, 2, 3 };

template<Triple const& t>
class Foo { };

Foo<t> f;

活生生的例子

不能将临时参数传递给引用模板参数的原因是该参数是引用。如果模板参数是const int&并且您尝试传递7. 例子

编辑

int三个s 和一个包含三个 s 的结构之间的区别在于int,所有类型的文字int实际上都是相同的值(所有出现7的只是七个),而对结构的每个构造函数调用在概念上都会创建一个新实例。举这个假设的例子:

template <Triple t>
struct Foo {};

Foo<Triple {1, 2, 3}> f1;
Foo<Triple {1, 2, 3}> f2;

我认为将这两个构造函数调用“匹配”到同一个模板实例化中会引入额外的复杂性。

于 2013-04-09T08:18:20.720 回答