简体   繁体   English

如何转换 C++17 的“if constexpr(std::is_literal_type<t> ::value)" 到 C++11 SFINAE 代码?</t>

[英]How to convert C++17's "if constexpr(std::is_literal_type<T>::value)" to C++11 SFINAE code?

Currently, I have this templated function in my codebase, which works pretty well in C++17:目前,我的代码库中有这个模板化的 function,它在 C++17 中运行良好:

/** This function returns a reference to a read-only, default-constructed
  * static singleton object of type T.
  */
template <typename T> const T & GetDefaultObjectForType()
{
   if constexpr (std::is_literal_type<T>::value)
   {
      static constexpr T _defaultObject = T();
      return _defaultObject;
   }
   else
   {
      static const T _defaultObject;
      return _defaultObject;
   }
}

The function has two problems, though:但是,function 有两个问题:

  1. It uses if constexpr , which means it won't compile under C++11 or C++14.它使用if constexpr ,这意味着它不会在 C++11 或 C++14 下编译。
  2. It uses std::is_literal_type , which is deprecated in C++17 and removed in C++20.它使用std::is_literal_type ,它在 C++17 中被弃用并在 C++20 中被移除。

To avoid these problems, I'd like to rewrite this functionality to use the classic C++11-compatible SFINAE approach instead.为了避免这些问题,我想重写此功能以改为使用经典的 C++11 兼容 SFINAE 方法。 The desired behavior is that for types that are constexpr -constructible (eg int , float , const char * ), the constexpr method will be called, and for types that are not (eg std::string ), the non- constexpr method will be called.期望的行为是,对于可构造的类型(例如intfloatconst char * ),将调用constexpr方法,而对于不可constexpr的类型(例如std::string ),将调用非constexpr方法叫做。

Here's what I've come up with so far (based on the example shown in this answer );到目前为止,这是我想出的(基于此答案中显示的示例); it compiles, but it doesn't work as desired:它可以编译,但不能按预期工作:

#include <string>
#include <type_traits>

namespace ugly_constexpr_sfinae_details
{
   template<int> struct sfinae_true : std::true_type{};

   template<class T> sfinae_true<(T::T(), 0)> is_constexpr(int);
   template<class> std::false_type is_constexpr(...);
   template<class T> struct has_constexpr_f : decltype(is_constexpr<T>(0)){};

   // constexpr version
   template<typename T, typename std::enable_if<true == has_constexpr_f<T>::value, T>::type* = nullptr> const T & GetDefaultObjectForType()
   {
      printf("constexpr method called!\n");

      static constexpr T _defaultObject = T();
      return _defaultObject;
   }

   // non-constexpr version
   template<typename T, typename std::enable_if<false == has_constexpr_f<T>::value, T>::type* = nullptr> const T & GetDefaultObjectForType()
   {
      printf("const method called!\n");

      static const T _defaultObject = T();
      return _defaultObject;
   }
}

/** Returns a read-only reference to a default-constructed singleton object of the given type */
template<typename T> const T & GetDefaultObjectForType()
{
   return ugly_constexpr_sfinae_details::GetDefaultObjectForType<T>();
}

int main(int, char **)
{
   const int         & defaultInt    = GetDefaultObjectForType<int>();          // should call the constexpr     function in the namespace
   const float       & defaultFloat  = GetDefaultObjectForType<float>();        // should call the constexpr     function in the namespace
   const std::string & defaultString = GetDefaultObjectForType<std::string>();  // should call the non-constexpr function in the namespace

   return 0;
}

When I run the program above, here is the output I see it print to stdout:当我运行上面的程序时,这是 output 我看到它打印到标准输出:

$ ./a.out 
const method called!
const method called!
const method called!

... but the output I would like it to emit is this: ...但是我希望它发出的 output 是这样的:

$ ./a.out 
constexpr method called!
constexpr method called!
const method called!

Can anyone point out what I'm doing wrong?谁能指出我做错了什么? (I apologize if it's something obvious; SFINAE logic is not a concept that comes naturally to me:/ ) (如果这很明显,我深表歉意;SFINAE 逻辑不是我自然而然的概念:/)

As @ Igor Tandetnik mentions in comments, static const T _defaultObject{};正如@Igor Tandetnik在评论中提到的那样, static const T _defaultObject{}; works in both cases and performs compile-time initialization when possible.在这两种情况下都有效,并在可能的情况下执行编译时初始化。 There's no need for constexpr .不需要constexpr

N3337 [basic.start.init] : N3337 [basic.start.init]

Constant initialization is performed:执行常量初始化

  • [...] [...]
  • if an object with static or thread storage duration is initialized by a constructor call, if the constructor is a constexpr constructor, if all constructor arguments are constant expressions (including conversions), and if, after function invocation substitution ([dcl.constexpr]), every constructor call and full-expression in the mem-initializers and in the brace-or-equal-initializers for non-static data members is a constant expression;如果具有 static 的 object 或线程存储持续时间由构造函数调用初始化,如果构造函数是 constexpr 构造函数,如果所有构造函数 arguments 都是常量表达式(包括转换),并且如果在 function 调用替换之后([dcl.constexpr]) ,内存初始化器和非静态数据成员的大括号或等于初始化器中的每个构造函数调用和完整表达式都是常量表达式;
  • if an object with static or thread storage duration is not initialized by a constructor call and if every full-expression that appears in its initializer is a constant expression.如果具有 static 或线程存储持续时间的 object 未由构造函数调用初始化,并且如果出现在其初始化程序中的每个完整表达式都是常量表达式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM