【问题标题】:Is There Anything Like a Templatized Case-Statement有没有类似模板化案例陈述的东西
【发布时间】:2015-04-26 06:45:41
【问题描述】:

所以我有这个非常丑陋的代码:

template <typename T>
std::conditional_t<sizeof(T) == sizeof(char),
                   char,
                   conditional_t<sizeof(T) == sizeof(short),
                                 short,
                                 conditional_t<sizeof(T) == sizeof(long),
                                               long,
                                               enable_if_t<sizeof(T) == sizeof(long long),
                                                           long long>>>> foo(T bar){return reinterpret_cast<decltype(foo(bar))>(bar);}

我正在使用嵌套的conditional_ts 来进行各种案例陈述。有没有什么可以更优雅地完成此任务,还是我需要编写自己的模板化案例陈述?

注意:我实际上知道reinterpret_cast 的这种使用是不好的:Why Doesn't reinterpret_cast Force copy_n for Casts between Same-Sized Types?

【问题讨论】:

  • 这段代码试图达到什么目的?
  • 你可以为 char,short,int, long long 做一个模板...
  • 为什么这感觉像是你的依赖类型编程语言?
  • @Slava Lol,我试图找到一种方法来清理这个:stackoverflow.com/a/28634468/2642059
  • @Slava:将任何类型的对象转换为包含相同字节值的相同大小(如果存在)的基本类型。

标签: c++ templates conditional case-statement nested-if


【解决方案1】:

我不得不这样做一次,所以我写了一个small wrapper 来巧妙地实现结果。您可以按如下方式使用它(请参阅here 进行测试)

template<class T>
typename static_switch<sizeof(T)
            ,int // default case
            ,static_case<sizeof(char),char>
            ,static_case<sizeof(short),short>
            ,static_case<sizeof(long),long>
            >::type foo(T bar){ ... }

在幕后,它几乎可以完成您已经拥有的功能,但是通过包装它,我们可以保持(更多)可读性。如果需要,还有一个版本允许您直接切换 T 类型。

编辑:@Deduplicator 的建议是它背后的代码

#include <type_traits>  

/* 
 * Select a type based on the value of a compile-time constant such as a 
 * constexpr or #define using static_switch. 
 */ 

template<int I,class T> 
struct static_case { 
    static constexpr int value = I; 
    using type = T; 
}; 

template<int I, class DefaultType, class Case1, class... OtherCases> 
struct static_switch{ 
    using type = typename std::conditional< I==Case1::value ,  
                    typename Case1::type, 
                    typename static_switch<I,DefaultType,OtherCases...>::type 
                     >::type; 
}; 

struct fail_on_default {};

template<int I, class DefaultType, class LastCase> 
struct static_switch<I,DefaultType,LastCase> { 
    using type = typename std::conditional< I==LastCase::value ,  
                    typename LastCase::type, 
                    DefaultType 
                     >::type; 

    static_assert(!(std::is_same<type, fail_on_default>::value),
                  "Default case reached in static_switch!");
}; 

【讨论】:

  • 您可能希望从此处从 github 导入您的代码...这将使答案完整。不管怎样,我只是在写这样的东西,但你明白了。
  • @Dan 我不确定我是否理解如何应用它。如果能插上的话,测试起来应该相当简单:auto val = foo(13.0);static_assert(is_same&lt;long long, decltype(val)&gt;::value);
  • @JonathanMee:顺便说一句,如果值可以从类型派生出来,它可以被简化,就像你的情况一样。
  • @Deduplicator 所以我刚刚整理了static_switch 的工作原理。你是什​​么意思,“它可以被简化?”
  • @JonathanMee:只要给出一个目标大小和一个类型列表就足够了,当应该返回第一个匹配类型时。
【解决方案2】:

类型标签:

template<class T>struct tag{using type=T;};

void_t(以 C++17 进入您附近的编译器):

template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;

enable_first_t 接受一包std::enable_if(注意缺少_t),并返回通过测试的第一个。可以用tag&lt;X&gt;代替std::enable_if&lt;true, X&gt;

template<class T,class=void>struct has_type:std::false_type{};
template<class T>struct has_type<T, void_t<typename T::type>>:std::true_type{};

namespace details {
  template<class, class...Ts>
  struct enable_first {};
  template<class T0, class...Ts>
  struct enable_first<std::enable_if_t< !has_type<T0>{} >, T0, Ts... >:enable_first<void, Ts...> {};
  template<class T0, class...Ts>
  struct enable_first<std::enable_if_t<  has_type<T0>{} >, T0, Ts...>:T0 {};
}

template<class...Ts>using enable_first_t=typename details::enable_first<void, Ts...>::type;

template<class T>
using result = enable_first_t<
  std::enable_if<sizeof(T)==sizeof(char), char>,
  std::enable_if<sizeof(T)==sizeof(short), short>,
  std::enable_if<sizeof(T)==sizeof(long), long>,
  tag<int> // default
>;

这很像switch,但语句是完整的布尔表达式。

live example

【讨论】:

  • 为什么不template&lt;class...&gt; using void_t = void;
【解决方案3】:

只要您了解相同大小的类型可能不是敞篷车的风险,您就可以简单地插入一个mpl::map..

typedef map<
      pair<int_<sizeof(char)>, char>,
      pair<int_<sizeof(short)>, short>,
      pair<int_<sizeof(int)>, int>,
      pair<int_<sizeof(long long)>, long long>
    > m;

例如

#include <algorithm>
#include <iostream>

#include <boost/mpl/at.hpp>
#include <boost/mpl/map.hpp>

using namespace boost::mpl;

typedef map<
      pair<int_<sizeof(char)>, char>,
      pair<int_<sizeof(short)>, short>,
      pair<int_<sizeof(int)>, int>,
      pair<int_<sizeof(long long)>, long long>
    > m;

template <typename T>
typename at<m, int_<sizeof(T)>>::type foo(T bar)
{ return reinterpret_cast<decltype(foo(bar))>(bar); }


struct doh
{
    std::string a, b, c;
};

int main()
{
    {
      char c;
      static_assert(std::is_same<decltype(foo(c)), char>::value, "error");
    }
    {
      short c;
      static_assert(std::is_same<decltype(foo(c)), short>::value, "error");
    }
    {
      int c;
      static_assert(std::is_same<decltype(foo(c)), int>::value, "error");
    }
    {
      long long c;
      static_assert(std::is_same<decltype(foo(c)), long long>::value, "error");
    }
    {
      double c;
      static_assert(std::is_same<decltype(foo(c)), long long>::value, "error");
    }    
    {
      doh c;
      static_assert(std::is_same<decltype(foo(c)), void_>::value, "error");
    }    
}

【讨论】:

  • 这看起来是一个很有前途的解决方案。但是我没有Boost。我想知道这是否可以通过std::map 以某种方式完成?
  • @JonathanMee,不能使用std::map,你可以使用switch,但这将是运行时测试而不是编译时...
【解决方案4】:

switch 语句的模板版本是一个专门的模板。

template<size_t n> struct matching_type;
template<> struct matching_type<sizeof(char)> { typedef char type; };
template<> struct matching_type<sizeof(short)> { typedef short type; };
template<> struct matching_type<sizeof(int)> { typedef int type; };
template<> struct matching_type<sizeof(long)> { typedef long type; };
template<> struct matching_type<sizeof(long long)> { typedef long long type; };

template<typename T>
matching_type<sizeof(T)>::type foo(T bar)
{
    return reinterpret_cast<decltype(foo(bar))>(bar);
}

【讨论】:

  • 请注意,在某些系统中,sizeof(int) 可能等于 sizeof(long),并且不会编译此代码
  • 没错,但是switch 语句也有同样的问题!
  • @borisbn 这也确实会产生编译时错误,希望我足够聪明以应对?
  • @Deduplicator:对问题发表评论,而不是在解决问题的答案上向 cmets 发送垃圾邮件不是更有效吗?
  • @IInspectable:这个问题没有受到那个问题的影响,只有我评论的两个解决方案有。
【解决方案5】:

可能是这样的:

template <size_t N> struct SuitablySized;

template<> struct SuitablySized<sizeof(char)> {
  typedef char type;
};
template<> struct SuitablySized<sizeof(short)> {
  typedef short type;
};
// Add more cases to taste

template <typename T>
typename SuitablySized<sizeof(T)>::type foo(T bar);

【讨论】:

    猜你喜欢
    • 2011-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-22
    • 2020-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多