【问题标题】:using result of constexpr function as a template parameter (clang vs gcc)使用 constexpr 函数的结果作为模板参数(clang vs gcc)
【发布时间】:2017-08-21 16:37:29
【问题描述】:

请看下面的代码,抱歉有点冗长,但我尽我所能用一个最小的例子重现了这个问题(还有一个live copy)。我基本上有一个元函数,它返回字符串文字的大小,以及包装它的 constexpr 函数。然后,当我在模板参数中调用这些函数时,gcc (5.4, 6.2) 对此感到满意,但在strsize(s) 的测试主体中带有“非类型模板参数不是常量表达式”的 clang (3.8, 3.9) barfs。如果我用str_size<S> 替换,那么两个编译器都很高兴。所以问题是:

  1. 这是 clang 的问题,还是我的代码的问题?

  2. 使用 constexpr 函数在 clang 和 gcc 上编译的方法是什么?

    template<size_t N> using string_literal_t = char[N];
    
    template<class T> struct StrSize; ///< metafunction to get the size of string literal alikes 
    
    /// specialize StrSize for string literals
    template<size_t N>
    struct StrSize <string_literal_t<N>>{ static constexpr size_t value = N-1; };
    
    /// template variable, just for convenience
    template <class T>
    constexpr size_t str_size = StrSize<T>::value;
    
    /// now do the same but with constexpr function
    template<class T>
    constexpr auto strsize(const T&) noexcept-> decltype(str_size<T>) {
       return str_size<T>;
    }
    
    template<class S, size_t... Is>
    constexpr auto test_helper(const S& s, index_sequence<Is...>) noexcept-> array<char, str_size<S>> {
       return {s[Is]...};
    }
    
    template<class S>
    constexpr auto test(const S& s) noexcept-> decltype(auto) {
    // return test_helper(s, make_index_sequence<str_size<S>>{}); // this work in both clang and gcc
       return test_helper(s, make_index_sequence<strsize(s)>{});  // this works only in gcc
    }
    
    auto main(int argc, char *argv[])-> int {
       static_assert(strsize("qwe") == 3, "");
       static_assert(noexcept(test("qwe")) == true, "");
    
       return 0;
    }
    

【问题讨论】:

  • str_size&lt;S1&gt;代替strsize(s1)有什么问题?
  • @max66 在这个特定的例子中没有什么问题。但它只能与大小参数化的类一起使用,如果大小是状态而不是类型的一部分,我需要在对象上调用函数,而不是在类型上。可能如果我找不到其他方法,我会使用 sfinae 来区分进入 test3 的类型,但我不喜欢这个想法。
  • 我遇到了和stackoverflow.com/questions/42997847一样的问题。

标签: c++ templates clang constexpr


【解决方案1】:

Clang 在这里是正确的。问题出在代码和 GCC 中,它们错误地接受了它。这已在 GCC 10 中修复:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66477

按照标准expr.const#5.12

表达式 E 是一个核心常量表达式,除非 E 的求值遵循抽象机的规则,将求值以下之一: ... 一个 id 表达式,它引用引用类型的变量或数据成员,除非该引用具有前面的初始化并且

  • 它可用于常量表达式或
  • 它的生命周期从 E 的评估开始; 而且这里编译器无法验证test(const S&amp; s)中引用的有效性。

其实这里有一篇不错的文章可以看:https://brevzin.github.io/c++/2020/02/05/constexpr-array-size/

关于你的另一个问题:

使用 constexpr 函数在 clang 和 gcc 上编译的方法是什么?

您可以将引用替换为std::array按值传递:

#include <array>
using namespace std;

template<class T> struct StrSize;

template<size_t N>
struct StrSize <array<char,N>>{ static constexpr size_t value = N-1; };

template <class T>
constexpr size_t str_size = StrSize<T>::value;

template<class T>
constexpr auto strsize(const T&) noexcept-> decltype(str_size<T>) {
   return str_size<T>;
}

template<class S, size_t... Is>
constexpr auto test_helper(const S& s, index_sequence<Is...>) noexcept-> array<char, str_size<S>> {
   return {s[Is]...};
}

constexpr auto test(array<char,4> s) noexcept-> decltype(auto) {
   return test_helper(s, make_index_sequence<strsize(s)>{});
}

int main() {
   static_assert(noexcept(test({"qwe"})) == true, "");
}

演示:https://gcc.godbolt.org/z/G8zof38b1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 2020-06-12
    • 1970-01-01
    相关资源
    最近更新 更多