【问题标题】:SFINAE constexpr with std::getSFINAE constexpr 与 std::get
【发布时间】:2016-08-26 10:09:48
【问题描述】:

这是Detecting constexpr with SFINAE 的后续问题。

我想检测元组的元素(或任何可以与std::get 一起使用的元素)是否为 constexpr。所以我写了以下类似于 Xeo 给出的助手:

template<size_t> struct sfinae_true : std::true_type{};

template<size_t N, class T>
auto check(const T& arg) -> sfinae_true<(std::get<N>(arg),N)>;

template<size_t N, class>
std::false_type check(...);

现在我的测试驱动代码:

int main()
{
    constexpr std::tuple<size_t, size_t> arg(4,5);
    typedef decltype(check<0,decltype(arg)>(arg)) is_cexpr;
    std::cout << "is constexpr? " << is_cexpr::value << '\n';
}

但是,这总是为我打印出false!为了检查由于某种原因错误重载并不总是被调用,我注释掉了错误重载并得到编译器错误:

注意:候选模板被忽略:替换失败 [with N = 0, T = const std ::tuple]:非类型模板参数不是常量表达式
自动检查(const T& arg) -> sfinae_true;

但是,我知道我可以调用 std::get&lt;N&gt;(arg) 并获得一个 constexpr 值:

template<size_t N>
class A{};

int main()
{
    constexpr std::tuple<size_t, size_t> arg(4,5);
    A<std::get<0>(arg)> a_val;
}

这编译得很好。

  • 为什么检查函数不能正确检测到常量表达式?
  • 我该如何解决这个问题?

我在 Ubuntu 16.04 上使用 Clang 3.8.0 对此进行了测试。

编辑:

作为基于 Sam 回答的进一步测试,我尝试了以下表格:

template<size_t N, class T>
auto check(const T& arg)
{
    return sfinae_true<(std::get<N>(arg)*0)>();
}

这完全摆脱了逗号运算符,GCC 5.4.0 编译得很好,但 Clang 3.8.0 仍然抱怨。有趣的是,Clang 强调 arg 本身不是 constexpr。

为什么这个问题仍然存在? constexpr 函数参数的规则是什么?

【问题讨论】:

  • 问题是arg参数不是constexpr...
  • (稍微扩展一下我认为@Jarod42 的含义:arg 作为函数参数永远不会被视为常量表达式,即使您提供给函数的参数是常量表达式。)
  • 如果此方法不起作用,我将如何检测函数调用的结果是否为 constexpr?显然,我可以通过构造 a_val 对象将元组用作 constexpr 值。
  • @helloworld922: constexpr int f(const std::tuple&lt;int&gt;&amp; t) {A&lt;std::get&lt;0&gt;(t)&gt; a_val; return 0;} 不起作用。

标签: c++ c++14 constexpr sfinae


【解决方案1】:

这看起来像是编译器问题。

template<size_t N, class T>
auto check(const T& arg) -> sfinae_true<(std::get<N>(arg),N)>;

gcc 编译失败:

t.C:8:61: 错误:模板参数 1 无效自动检查(const T& arg) -> sfinae_true;

但是在稍微调整之后,我使用 gcc 6.1.1 得到了预期的结果:

#include <tuple>
#include <type_traits>
#include <iostream>

template<size_t> struct sfinae_true : std::true_type{};

template<size_t N, class T>
auto check(const T& arg)
{
    return sfinae_true<(std::get<N>(arg),N)>();
}

template<size_t N, class>
std::false_type check(...);

int main()
{
    constexpr std::tuple<size_t, size_t> arg(4,5);
    typedef decltype(check<0,decltype(arg)>(arg)) is_cexpr;
    std::cout << "is constexpr? " << is_cexpr::value << '\n';
}

这会导致:

is constexpr? 1

请注意,C++11 之前的常量表达式中不允许使用逗号。可能是那个时代遗留下来的东西……

【讨论】:

  • 我在 clang 3.8.0 中尝试了您的调整,但它不起作用,但它适用于 GCC 5.4.0。有趣的是,clang 突出显示为 non-constexpr 的部分不是逗号运算符,而是 arg 是 non-constexpr 的事实(我通过删除逗号运算符对此进行了测试)。
  • 强制行为在您的版本中有所不同。 SFINAE 不适用于推断的返回类型; SFINAE 仅在直接上下文中,而返回类型推导需要对函数模板进行相当深入的实例化。
  • 有趣的是,@dyp,最终结果是预期的结果。非 constexpr 参数不会导致编译错误,而是预期的默认模板。在我看来,模板函数的返回类型是在声明时推导出来的,然后正常参与 SFINAE 解析。
  • 但是,clang++4.0 拒绝编译您的程序。 gcc 报告 1 无论arg 是否为constexpr
猜你喜欢
  • 2017-06-02
  • 2013-02-20
  • 2020-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多