VS2015中类型特征的奇怪错误

时间:2017-01-05 03:41:14

标签: c++ c++11 visual-studio-2015 typetraits

我花了大约一周的时间为游戏引擎开发一个类型无关的日志记录系统,我正在使用类型特征来控制应该记录不同模板参数的方式。联盟和类类型忽略这些检查(以某种方式)并触发错误C2679。

我想知道为什么会发生这种情况以及我可以做些什么来解决它,除了将我的模板分解成许多小函数。

if (std::is_arithmetic<loggableType>::value)
{
    if (destination == DESTINATIONS::CONSOLE)
    {
        *printStreamPttr << "logging " << typeid(loggableType).name() << " with value " << dataLogging << '\n';
        ConsolePrinter::OutputText(printStreamPttr);
    }

    else
    {
        logFileStream.open(logFilePath);
        logFileStream << "logging " << typeid(loggableType).name() << " with value " << dataLogging << '\n';
        logFileStream.close();
    }
}

1 个答案:

答案 0 :(得分:4)

if不会导致编译时分支。无论条件如何,两个分支都必须有效。

以下是我在这些情况下有用的dispatch函数:

template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;

constexpr index_t<0> dispatch_index() { return {}; }
template<class B0, class...Bools,
    class=std::enable_if_t<B0::value>
>
constexpr index_t<0> dispatch_index(B0, Bools...)
{ return {}; }

template<class B0, class...Bools,
    class=std::enable_if_t<!B0::value>
>
constexpr auto dispatch_index(B0, Bools...bools)
{ return index_t< dispatch_index(bools...)+1 >{}; }

template<class...Bools>
constexpr auto dispatch( Bools...bools ) {
  using get_index = decltype(dispatch_index(bools...));
  return [](auto&&...args){
    using std::get;
    return get< get_index::value >(
      std::forward_as_tuple( decltype(args)(args)... )
    );
  };
}

此实用程序函数在一组选项之间执行编译时调度。

以下是一个例子:

    union bob {};
    bob b;
    dispatch( std::is_arithmetic<decltype(b)>{} )
    (
        [&](auto&& value) { std::cout << value << "\n"; },
        [&](auto&& value) { std::cout << "not a number\n"; }
    )
    (b);

dispatch( std::is_arithmetic<decltype(b)>{} )按值(实际上是任意数量)获取真实或虚假类型。它找到了传递给它的第一个truthy类型。

然后返回一个带有任意数量参数的lambda,并返回与第一个truthy参数对应的一个参数。如果调度没有真正的论点,它假装它有一个真正的论点&#34;在结束之后&#34;并基于此发送。

    dispatch( std::is_arithmetic<decltype(b)>{} )

由于bob不是is_arithmetic,这是一种假类型。因此,dispatch将返回一个lambda,返回其第二个参数。

    (
        [&](auto&& value) { std::cout << value << "\n"; },
        [&](auto&& value) { std::cout << "not a number\n"; }
    )

在这种情况下,我们传递两个lambda,两个都有相同的签名。

我们将返回第二个。

    (b);

然后我们传递b。第一个期望值传递给ostream&::operator<<的lambda永远不会被评估,因此union bob不支持它的事实并不重要。

live example,并使用MSVC2015

以上是有效的C ++ 14,我相信我避免了所有阻止它编译的MSVC弱点。

要将类型传递到lambda中,您可能需要使用以下内容:

template<class T>struct tag_t{using type=T; constexpr tag_t(){};};
template<class T>constexpr tag_t<T> tag{};
template<class Tag>using type_t = typename Tag::type;
#define GET_TAGGED_TYPE(...) type_t< std::decay_t<decltype(__VA_ARGS__)> >;

然后您的代码如下:

dispatch(std::is_arithmetic<loggableType>{})
(
  [&](auto tag){
    using loggableType=GET_TAGGED_TYPE(tag);
    if (destination == DESTINATIONS::CONSOLE) {
      *printStreamPttr << "logging " << typeid(loggableType).name() << " with value " << dataLogging << '\n';
      ConsolePrinter::OutputText(printStreamPttr);
    } else {
      logFileStream.open(logFilePath);
      logFileStream << "logging " << typeid(loggableType).name() << " with value " << dataLogging << '\n';
      logFileStream.close();
    }
  },
  [&](auto non_arith_tag) {
    // nothing?
  }
)
( tag<loggableType> );