在C ++ 14

时间:2016-06-05 11:36:49

标签: c++ c++11 template-meta-programming constexpr

我有一些通用代码,它们想知道何时传递了一系列对象,这些对象的数量在编译时是已知的,因为它可以选择另一种算法策略。为此,我尝试编写一个has_constexpr_size(T) constexpr函数,如下所示,它试图探测T的size()成员函数,看它是否可以作为constexpr执行。

请注意,与通常的“我可以检测到constexpr执行上下文吗?”存在一个关键区别。问题是因为某些STL容器(如array<T> 始终提供了constexpr可用的size()函数,而其他STL容器(如initializer_list<T>)获得了constexpr-available size()函数当且仅当初始化列表本身是constexpr时(因为实现依赖于内部指针对,而那些需要constexpr才能使size()具有all-constexpr输入)。因此,has_constexpr_size(T)函数采用正在测试的T类型的实例,以便它可以检测这两种情况。

这是一个测试用例:

#include <array>
#include <type_traits>
#include <utility>
#include <vector>

namespace type_traits
{
  namespace detail
  {
    template <size_t N> struct Char
    {
      char foo[N];
    };
    template <class T> constexpr inline Char<2> constexpr_size(T &&v) { return (v.size(), true) ? Char<2>() : throw 0; }
    template <class T> inline Char<1> constexpr_size(...) { return Char<1>(); }
  }
  //! Returns true if the instance of v has a constexpr size()
  template <class T> constexpr inline bool has_constexpr_size(T &&v)
  {
    return noexcept(detail::constexpr_size<T>(std::forward<T>(v)));
  }

  // Non-constexpr array (always has a constexpr size())
  auto ca=std::array<int, 2>();
  // Constexpr initializer_list (has constexpr size()). Note fails to compile on VS2015 as its initializer_list isn't constexpr capable yet
  constexpr std::initializer_list<int> cil{1, 2};
  // Non-constexpr initializer_list (does not have constexpr size())
  std::initializer_list<int> il{1, 2};
  // Non-constexpr vector (never has constexpr size())
  std::vector<int> vec{1, 2};

  // Passes on GCC 4.9 and clang 3.8
  static_assert(ca.size(), "non-constexpr array size constexpr");
  // Passes on GCC 4.9 and clang 3.8
  static_assert(cil.size(), "constexpr il size constexpr");
  // Fails as you'd expect everywhere with non-constexpr il error
  //static_assert(il.size(), "non-constexpr il size constexpr");

  // Passes on GCC 4.9, fails on VS2015 and clang 3.8
  static_assert(has_constexpr_size(ca), "ca");  // Should NOT fail on VS2015 and clang 3.8
  // Fails on GCC 4.9 and clang 3.8. VS2015 doesn't apply.
  static_assert(has_constexpr_size(cil), "cil");  // FAILS, and it should not!
  // Passes, correct
  static_assert(!has_constexpr_size(il), "il");
  // Passes, correct
  static_assert(!has_constexpr_size(vec), "vec");

  constexpr bool test()
  {
    return has_constexpr_size(std::initializer_list<int>{1, 2});
  }
  constexpr bool testval=test();
  // Fails on GCC 4.9 and clang 3.8. VS2015 doesn't apply.
  static_assert(testval, "test()");
}

您会注意到array<T>::size()和constexpr initializer_list<T>::size()的直接静态断言在所有编译器上都能正常工作,正如您所期望的那样。我的has_constexpr_size(T)函数适用于array<T>,但仅适用于GCC 4.9,我认为这是因为GCC特别容忍constexpr实现。我的has_constexpr_size(T)函数在所有编译器上的constexpr initializer_list<T>都失败了。

所以我的问题是:

  1. 是否可以编写has_constexpr_size(T)函数来正确检测size()和constexpr array<T>以及提供constexpr的任何其他类型的constexpr可用initializer_list<T>成员函数可用的size()功能?

  2. 如果当前在C ++或当前编译器中无法使用(1),则可以编写has_constexpr_size(T)函数,该函数正确检测size()的constexpr可用array<T>成员函数和任何其他类型提供始终constexpr可用的size()函数? 注意此解决方案不会检查是否存在一些size()函数,而是检查它是否为constexpr-available。所以has_constexpr_size(std::vector<int>())将是假的。

2 个答案:

答案 0 :(得分:0)

修改:如果您将std::string替换为int而不是std::array<std::string, 2>,则以下答案可能错误 static_assert(static_cast<std::array<int, 2> *>(0)->size(), "foo"); // passes static_assert(static_cast<std::array<std::string, 2> *>(0)->size(), "foo"); // fails 您发现它失败了。由于某些对我没有意义的原因,C ++ 14将以下内容视为:

size()

我正式傻眼了。无论如何,为了历史的兴趣,我会留下以下答案。

我有自己的问题2的工作解决方案,其中使用表达式SFINAE我可以检测array<>的非零constexpr可用vector<>成员函数,并正确排除initializer_list<>的成员函数,但它得到constexpr #include <array> #include <type_traits> #include <utility> #include <vector> namespace type_traits { namespace detail { template <size_t N> struct Char { char foo[N]; }; // Overload only available if a default constructed T has a constexpr-available non-zero size() template <class T, size_t N = T{}.size() + 1> constexpr inline Char<N> constexpr_size(const T &) { return Char<N>(); } template <class T> constexpr inline Char<1> constexpr_size(...) { return Char<1>(); } } //! Returns true if the instance of v has a constexpr size() template <class T> constexpr inline bool has_constexpr_size(const T &v) { return sizeof(detail::constexpr_size<typename std::decay<T>::type>( std::move(v))) > 1; } // Non-constexpr array (always has a constexpr size()) auto ca = std::array<int, 2>(); // Constexpr initializer_list (has constexpr size()). Note fails to compile on // VS2015 as its initializer_list isn't constexpr constructible yet #ifndef _MSC_VER constexpr std::initializer_list<int> cil{1, 2}; #endif // Non-constexpr initializer_list (does not have constexpr size()) std::initializer_list<int> il{1, 2}; // Non-constexpr vector (never has constexpr size()) std::vector<int> vec{1, 2}; // Correct on GCC 4.9 and clang 3.8 and VS2015 static_assert(ca.size(), "non-constexpr array size constexpr"); // Correct on GCC 4.9 and clang 3.8. #ifndef _MSC_VER static_assert(cil.size(), "constexpr il size constexpr"); #endif // Fails as you'd expect everywhere with non-constexpr il error // static_assert(il.size(), "non-constexpr il size constexpr"); // Correct on GCC 4.9 and clang 3.8 and VS2015 static_assert(has_constexpr_size(ca), "ca"); // Incorrect on GCC 4.9 and clang 3.8 and VS2015 #ifndef _MSC_VER static_assert(!has_constexpr_size(cil), "cil"); // INCORRECT! #endif // Correct on GCC 4.9 and clang 3.8 and VS2015 static_assert(!has_constexpr_size(il), "il"); // Correct on GCC 4.9 and clang 3.8 and VS2015 static_assert(!has_constexpr_size(vec), "vec"); constexpr bool test_ca() { return has_constexpr_size(std::array<int, 2>{1, 2}); } constexpr bool testca = test_ca(); // Correct on GCC 4.9 and clang 3.8 and VS2015 static_assert(testca, "testca()"); constexpr bool test_cil() { return has_constexpr_size(std::initializer_list<int>{1, 2}); } constexpr bool testcil = test_cil(); // Incorrect on GCC 4.9 and clang 3.8 and VS2015 static_assert(!testcil, "testcil()"); // INCORRECT! } 错误。确认在GCC 4.9和clang 3.8和VS2015上工作:

{{1}}

所以我们现在需要的是一些过滤掉非constexpr输入类型的方法,这样我们就可以正确地为非constexpr输入返回false。我怀疑这是与stackoverflow上其他地方相同的未解决问题:(

非常欢迎任何进一步的进步或想法。

答案 1 :(得分:0)

如果我正在解释这个建议:

std::is_constant_evaluated()

正确地讲,自C ++ 14起,该标准的措辞甚至没有阐明在编译时必须知道什么,什么不是。而且,无论如何,在C ++ 20中添加的“魔术库函数”无法用该语言本身实现。

GCC 9的实现是:

constexpr inline bool
is_constant_evaluated() noexcept
{ return __builtin_is_constant_evaluated(); }

这是另一种迹象(尽管不是肯定的),表明不能使用其他语言构造来实现。