constexpr std :: string_view :: find_last_of在使用libstdc ++ 9的clang 8上不起作用

时间:2019-06-06 20:41:31

标签: c++ g++ clang c++17 constexpr

以下代码在g ++ 9下使用标志-std=c++17进行编译,但不对具有相同标志的clang 8进行编译:

#include <string_view>
#include <cstdlib>


int main() {

        constexpr std::string_view hello = "hello";
        constexpr size_t last_l = hello.find_last_of("lo");
}

错误消息如下:

test.cpp:8:19: error: constexpr variable 'last_l' must be initialized by a constant expression
        constexpr size_t last_l = hello.find_last_of("lo");
                         ^        ~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/char_traits.h:349:9: note: cast from 'void *' is not allowed in a constant expression
        return static_cast<const char_type*>(__builtin_memchr(__s, __a, __n));
               ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/string_view.tcc:150:12: note: in call to 'find(&"lo"[0], 2, "hello"[4])'
              if (traits_type::find(__str, __n, this->_M_str[__size]))
                  ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/string_view:402:22: note: in call to '&hello->find_last_of(&"lo"[0], 18446744073709551615, 2)'
      { return this->find_last_of(__str, __pos, traits_type::length(__str)); }
                     ^
test.cpp:8:34: note: in call to '&hello->find_last_of(&"lo"[0], 18446744073709551615)'
        constexpr size_t last_l = hello.find_last_of("lo");
                                        ^
test.cpp:10:16: error: static_assert expression is not an integral constant expression
        static_assert(last_l == 3);
                      ^~~~~~~~~~~
test.cpp:10:16: note: initializer of 'last_l' is not a constant expression
test.cpp:8:19: note: declared here
        constexpr size_t last_l = hello.find_last_of("lo");
                         ^
2 errors generated.

看着错误消息,看起来它调用了std::char_traits<char>::find,其中according to cppreference应该是constexpr而不是问题。但是,查看char_traits.h中的实现,虽然它标记为constexpr,但似乎并没有遵循constexpr规则:

static _GLIBCXX17_CONSTEXPR const char_type*
find(const char_type* __s, size_t __n, const char_type& __a)
{
  if (__n == 0)
    return 0;
#if __cplusplus >= 201703L
  if (__builtin_constant_p(__n)
      && __builtin_constant_p(__a)
      && __constant_char_array_p(__s, __n))
    return __gnu_cxx::char_traits<char_type>::find(__s, __n, __a);
#endif
  return static_cast<const char_type*>(__builtin_memchr(__s, __a, __n));
}

但是,即使在clang诊断消息__builtin_memchr中似乎返回了void*,然后将其转换为char*,g ++似乎也没有问题,似乎是不允许的(see bullet 14)。

遵循同样的思路,以下示例也使用g ++而不是clang进行编译:

#include <string>

int main() {
        constexpr const char* asdf = "asdf";
        constexpr const char* s_ptr = std::char_traits<char>::find(asdf, 2, 's');
}

出现错误消息:

test_traits.cpp:5:24: error: constexpr variable 's_ptr' must be initialized by a constant expression
        constexpr const char* s_ptr = std::char_traits<char>::find(asdf, 2, 's');
                              ^       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/char_traits.h:349:9: note: cast from 'void *' is not allowed in a constant expression
        return static_cast<const char_type*>(__builtin_memchr(__s, __a, __n));
               ^
test_traits.cpp:5:32: note: in call to 'find(&"asdf"[0], 2, 's')'
        constexpr const char* s_ptr = std::char_traits<char>::find(asdf, 2, 's');
                                      ^
1 error generated.

这似乎既是stdc++的错误(没有适当地std::char_traits<_>::find的{​​{1}},又是g ++的编译错误。

有什么我想念的吗?另外,有解决方法吗?我遇到了尝试使用magic enum的情况,并且希望在clang环境中使用它。

更新(6/7/19)

@cpplearner指出,似乎clang应该采用constexpr,有趣的是,不是似乎是问题所在。以下代码与return __gnu_cxx::char_traits<char_type>::find(__s, __n, __a);中的功能完全相同,但没有该常量分支,但在g ++中仍然可以正常编译,这使我怀疑g ++是否甚至采用了该路径:

std::char_traits<char>::find

这仍然可以在g ++中编译,并且在clang中失败,并显示相同的错误。

更新2(6/7/19)

更多研究证实了@cpplearner的评论:

#include <cstddef>

static constexpr  const char*
find_noconst(const char* __s, size_t __n, const char& __a)
{
        if (__n == 0)
                return 0;
        return static_cast<const char*>(__builtin_memchr(__s, __a, __n));
}

int main() {
        constexpr const char* asdf = "asdf";
        constexpr const char* s_ptr = find_noconst(asdf, 2, 's');
}

使用gcc,它会打印#include <cstddef> #include <string> #include <iostream> static constexpr const char* branch_test(const char* __s, size_t __n, const char& __a) { if (__n == 0) return 0; if (__builtin_constant_p(__n) && __builtin_constant_p(__a) && std::__constant_char_array_p(__s, __n)) return "took branch"; return "did not take branch"; } int main() { constexpr const char* test = branch_test("asdf", 2, 's'); std::cout << test << std::endl; } ,而使用clang时,它会打印took branch

详细说明:

did not take branch

在clang上打印#include <string> #include <iostream> int main() { constexpr bool test = std::__constant_char_array_p("test", 2); std::cout << test << std::endl; } ,在g ++上打印0

深入研究该实现之后,看来这实际上是一个clang错误。我能找到的差异的最小例子是:

1

在c上打印#include <iostream> constexpr bool is_const_char(const char* a, size_t idx) { return __builtin_constant_p(a[idx]); } int main() { constexpr bool test = is_const_char("test", 0); std::cout << test << std::endl; } ,在gcc上打印0。对我来说最有趣的部分是实际的工作方式:

1

该程序在clang和g ++上始终打印#include <iostream> int main() { constexpr const char* str = "test"; constexpr bool test1 = __builtin_constant_p(str[0]); constexpr bool test2 = __builtin_constant_p("test"[0]); std::cout << test1 << ", " << test2 << std::endl; }

0 个答案:

没有答案