【问题标题】:Calling constexpr in default template argument在默认模板参数中调用 constexpr
【发布时间】:2012-05-23 13:36:39
【问题描述】:

在 C++11 中,我使用 constexpr 函数作为模板参数的默认值 - 它看起来像这样:

template <int value>
struct bar
{
    static constexpr int get()
    {
        return value;
    }
};

template <typename A, int value = A::get()>
struct foo
{
};

int main()
{
    typedef foo<bar<0>> type;

    return 0;
}

G++ 4.5 和 4.7 编译这个,但 Clang++ 3.1 没有。来自 clang 的错误信息是:

clang_test.cpp:10:35: error: non-type template argument is not a constant expression
template <typename A, int value = A::get()>
                                  ^~~~~~~~
clang_test.cpp:17:19: note: while checking a default template argument used here
        typedef foo<bar<3>> type;
                ~~~~~~~~~^~
clang_test.cpp:10:35: note: undefined function 'get' cannot be used in a constant expression
template <typename A, int value = A::get()>
                                  ^
clang_test.cpp:4:23: note: declared here
        static constexpr int get()
                             ^
1 error generated.

哪个是正确的?

【问题讨论】:

  • 我对 clang 3.1 有同样的问题。关于如何解决它的任何想法?我是否必须使用模板元编程而不是 constexpr,才能在模板实例化中“调用”它?

标签: c++ gcc c++11 clang constexpr


【解决方案1】:

LLVM IRC 频道的 Richard Smith(zygoloid)就这个问题与我进行了简短的交谈,这是您的答案

<litb> hello folks
<litb> zygoloid, what should happen in this case?
<litb> http://stackoverflow.com/questions/10721130/calling-constexpr-in-default-template-argument
<litb> it seems to be clang's behavior is surprising
<litb> zygoloid, i cannot apply the "point of instantiation" rule to constexpr 
  function templates. if i call such a function template, the called definition's 
  POI often is *after* the specialization reference, which means at the point of 
  the call, the constexpr function template specialization is "undefined".
<zygoloid> it's a horrible mess. Clang does not do what the standard intends, but 
  as you note, the actual spec is gloriously unclear
<d0k> :(
<zygoloid> we should instantiate bar<3>::get(), because it is odr-used, but we 
  don't, because we incorrectly believe it's used in an unevaluated context
<zygoloid> conversely, the point of instantiation is too late :/
<zygoloid> PR11851

因此,有时 Clang 实例化调用的函数模板或类模板的成员函数,但它们的实例化为时已晚,调用无法看到,而在其他情况下,它甚至不实例化它们,因为它认为它永远不需要他们(未评估的上下文)。

【讨论】:

【解决方案2】:

我认为 GCC Clang 是正确的

引自 n3290:

14.3.2 模板非类型参数 [temp.arg.nontype]

  1. 非类型、非模板模板参数的模板参数应为以下之一:
    • 对于整数或枚举类型的非类型模板参数,模板参数类型的转换 > 常量表达式 (5.19);要么
    • ...

编辑:5.19 3

文字常量表达式是纯右值的核心常量表达式 文字类型,但不是指针类型。一个整数常量表达式是 整数或无范围枚举的字面常量表达式 类型。 [注意:这样的表达式可以用作数组边界(8.3.4, 5.3.4),作为位域长度 (9.6),作为枚举器初始化器,如果基础类型不固定 (7.2),作为空指针常量 (4.10), 并作为对齐 (7.6.2)。 —尾注] 转换后的常量表达式 T 类型的字面常量表达式,隐式转换为 类型 T,其中允许隐式转换(如果有) 文字常量表达式和隐式转换序列 仅包含用户定义的转换,左值到右值的转换 (4.1)、积分促销 (4.5) 和积分转换 (4.7) 其他 而不是缩小转化率 (8.5.4)。

[ 注意:此类表达式可以用作 case 表达式 (6.4.2),如果基础类型是固定的 (7.2),则可以用作枚举器初始化器,以及用作整数或枚举非类型模板参数 (14.3)。 ——尾注]

【讨论】:

  • @cschwan:对不起,我错了,还没有读完 5.19。 Clang 是正确的,请参阅我的编辑。
  • 好的,帮帮我一下:在我的情况下,value 的默认值必须是“转换后的常量表达式”。 5.19.3 现在声明 A::get() 不是常量表达式,因为它不是其中列出的任何项目,对吧?
  • A::get()core constant expresion(在 5.19.2 中描述),但不是 converted constant expression
  • 为什么不是转换后的常量表达式?
  • 文字常量表达式是文字非指针类型的纯右值核心常量表达式。正如您已经同意 A::get() 是一个核心常量表达式,并且我认为没有任何论据表明它是文字非指针类型的纯右值,A::get() 是一个文字常量表达式。根据您引用的文本,没有任何隐式转换的文字常量表达式也是转换后的常量表达式。我错过了什么吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-28
  • 1970-01-01
相关资源
最近更新 更多