C++:在 MSVC 2010 中使用 #if std::is_fundamental<T>::value 进行条件编译

Posted

技术标签:

【中文标题】C++:在 MSVC 2010 中使用 #if std::is_fundamental<T>::value 进行条件编译【英文标题】:C++: Use #if std::is_fundamental<T>::value for conditional compilation in MSVC 2010 【发布时间】:2012-07-12 12:40:18 【问题描述】:

在我的模板中,我需要根据类型名是否为基本类型来使用不同的代码部分。

编译此代码会在 MSVC 中给出 C4067(预处理器指令后出现意外标记 - 需要换行符):

template <typename T>
void MyClass<T>::foo()

// ... some code here
#if std::is_fundamental<T>::value
    if(m_buf[j] < m_buf[idx_min])
        idx_min = j;
#else
    const ASSortable& curr = dynamic_cast<ASSortable&>(m_buf[j]);
    const ASSortable& curr_min = dynamic_cast<ASSortable&>(m_buf[idx_min]);
    // error checking removed for simplicity
    if(curr.before(curr_min))
        idx_min = j;

#endif

该模板适用于原始数据类型和我自己的(从 ASSortable 派生的)数据类型,并且从模板实例化代码中抛出错误:

template class MyClass<char>;

尝试将预编译器表达式修改为此也不起作用:

#if std::is_fundamental<T>::value == true

并产生完全相同的警告。

任何想法如何使此代码无警告?

编辑想到的另一件事是将其转换为运行时检查并使用“常量 if 表达式”警告..​​.真的没有办法优雅地做到这一点single 函数,没有特化,也没有额外的臃肿?

Edit #2 所以我解决这个问题的方法(这很明显,但不知怎的让我逃脱了......)是定义一个 bool ASSortable::operator&lt;(const ASSortable&amp; _o) const return this-&gt;before(_o);; 来完成这项工作并使代码干净(一次再次)。

我的代码中不再有 ifs 或 #ifdefs 或任何类似的混乱!

不敢相信我什至问了这个问题,因为它的答案如此简单明了:(

【问题讨论】:

你不能使用MyClass的特化吗? 我试图不让太多的专业化污染实现。考虑到 MyClass 将专门用于许多基本类型,并且代码很快就会变得混乱(专业化将涵盖 char/short/int/long/long long 和它们的无符号对应物,float/double/long double)所以我d 真的宁愿只进行条件编译,而不是为基本类型的每个特化复制代码块 @YePhIcK 您始终可以使用enable_if 来防止将一个类专门化为一个专门的重载。我的解决方案就是这样做的。 以下所有解决方案都是有效的,但我可能最终不会使用它们,原因如下:调试程序至少是编写它们的两倍,所以如果你正在编写一个程序在你能力的边缘......我相信不需要专家水平来阅读/理解的简单代码。太糟糕了,我的问题没有优雅的解决方案。 为什么还要定义ASSortable 接口?我们已经有operator&lt; 【参考方案1】:

解决该问题的常见模式是将函数移至专用的基类并滥用继承将其带入您的范围:

template <typename T, bool is_fundamental>
struct Foo_impl 
   void foo() 
   
;
template <typename T>
struct Foo_impl<T,true>

   void foo()               // is fundamental version
   
;
template <typename T>
class Foo : public Foo_impl<T, std::is_fundamental_type<T>::value> 
   // ...
;

另一种方法是将它们实现为类中的私有函数,并根据特征在内部从foo 分派给它们。这是一个非常简单且更清洁的解决方案,但如果foo_impl 的两个版本之一无法编译,则会失败。在这种情况下,您可以使用,因为其他人建议使用模板成员函数来解决,我仍然会提供非模板化的 foo 作为公共接口,转发到私有 foo_impl 模板.原因是其中的template 有一个hack 条件编译的实现细节,而不是接口的一部分。您不希望用户代码使用与您自己的类的类型不同的模板参数调用该成员函数。借用pmr的回答:

template <typename T>
struct Foo

  template <typename U = T, 
            typename std::enable_if< 
              std::is_fundamental<U>::value, int >::type* _ = 0
           >
  void foo() 
    std::cout << "is fundamental" << std::endl;
  
//...

该解决方案允许用户编写如下代码:

Foo<int> f;
f.foo<std::string>();

它将实例化一个您不需要也不想要的函数,并将执行您不想要的逻辑。即使用户不试图欺骗您的类,接口中的模板这一事实也可能令人困惑,并使用户认为可以为不同的类型调用它。

【讨论】:

是的,这是我的解决方案的一个真正问题。你的 API 更安全、更清洁。【参考方案2】:

预处理器在编译的早期运行,在编译器分析类型并知道std::is_fundamental&lt;T&gt;::value的含义之前,因此它不能这样工作。

改为使用专业化:

template<bool> void f();

template<> void f<true>() 
    if(m_buf[j] < m_buf[idx_min])
        idx_min = j;


template<> void f<false>() 
    const ASSortable& curr = dynamic_cast<ASSortable&>(m_buf[j]);
    const ASSortable& curr_min = dynamic_cast<ASSortable&>(m_buf[idx_min]);
    // error checking removed for simplicity
    if(curr.before(curr_min))
        idx_min = j;


template <typename T>
void MyClass<T>::foo()

// ... some code here
    f<std::is_fundamental<T>::value>();

编辑:您可能需要将f 设为成员函数,但由于MyClass&lt;T&gt; 是非专用模板,因此无法直接实现。您可以将f 设为全局,将调用委托给MyClass 的正确成员。但是,还有另一种方法。

使用重载,这变成:

void MyClass<T>::f(const true_type&) 
    if(m_buf[j] < m_buf[idx_min])
        idx_min = j;


void MyClass<T>::f(const false_type&) 
    const ASSortable& curr = dynamic_cast<ASSortable&>(m_buf[j]);
    const ASSortable& curr_min = dynamic_cast<ASSortable&>(m_buf[idx_min]);
    // error checking removed for simplicity
    if(curr.before(curr_min))
        idx_min = j;


template <typename T>
void MyClass<T>::foo()

// ... some code here
    f(std::is_fundamental<T>::type());

【讨论】:

@DavidRodríguez-dribeas:不正确,除非您明确实例化它们,否则它们不会被实例化。 对不起,我不知道我在想什么。【参考方案3】:

您正在混淆编译状态。预处理器在实际编译器之前运行,并且不知道类型或模板。它只是执行(非常)复杂的文本替换。

在当前的 C++ 中没有诸如static if1 之类的东西,所以你必须求助于不同的方法来启用条件编译。对于函数,我更喜欢enable_if

#include <type_traits>
#include <iostream>

template <typename T>
struct Foo

  template <typename U = T, 
            typename std::enable_if< 
              std::is_fundamental<U>::value, int >::type = 0
           >
  void foo() 
    std::cout << "is fundamental" << std::endl;
  

  template <typename U = T, 
            typename std::enable_if< 
              !(std::is_fundamental<U>::value), int >::type = 0
           >
  void foo() 
    std::cout << "is not fundamental" << std::endl;
  
;


struct x ;

int main()

  Foo<int> f; f.foo();
  Foo<x> f2; f2.foo();
  return 0;


1 参考资料:

Video:静态如果由 Alexandrescu 在 Going Native 中提出。

n3322:Walter E. Brown 对static if的提议

n3329:Sutter、Bright 和 Alexandrescu 对static if 的提议

【讨论】:

太糟糕了static if 不会很快可用:( @YePhIcK: static if 有它自己的问题。它将允许某些可能使用户感到困惑并且难以被编译器跟踪的构造。我不记得细节了,但归结为这样一个事实,即在模板实例化(查找的第二阶段)之前,该类型的实际内容是未知的(static if 语句可能会更改某些字段或属性)跨度> 【参考方案4】:

正如它所说的,你不能在预处理器指令中使用::。实际上,在#if 之后唯一可以使用的是在编译时之前定义的常量表达式。你可以找到一些信息here

【讨论】:

【参考方案5】:

std::is_fundamental&lt;T&gt;::value == true 不能在预处理时使用。我想您将不得不对 std::enable_if 使用一些 SFINAE 技巧:

template <typename T>
typename std::enable_if<std::is_fundamental<T>::value, void>::type 
MyClass<T>::foo()

    // ... some code here

    if(m_buf[j] < m_buf[idx_min])
        idx_min = j;



template <typename T>
typename std::enable_if<!std::is_fundamental<T>::value, void>::type 
MyClass<T>::foo()

    // ... some code here

    const ASSortable& curr = dynamic_cast<ASSortable&>(m_buf[j]);
    const ASSortable& curr_min = dynamic_cast<ASSortable&>(m_buf[idx_min]);
    // error checking removed for simplicity
    if(curr.before(curr_min))
        idx_min = j;

【讨论】:

Afaik 除非您使 foo 依赖于模板参数,否则这是行不通的。

以上是关于C++:在 MSVC 2010 中使用 #if std::is_fundamental<T>::value 进行条件编译的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 MSVC 在 C++ 中定义外部 C 结构返回函数?

MSVC10 Visual Studio 2010 是不是支持 C++ 显式转换运算符

在MSVC ++ 2010中禁用警告

如何使用 system() 函数 (MSVC) 编译 c++ 代码?

C++ - 在 Windows 上使用 GCC 而不是 MSVC 值得吗? [关闭]

Qt Creator 2.6.1 + Qt 5 + C++11 + MSVC2010 编译器