2

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

编译此代码会在 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 表达式”警告..​​.真的没有办法在没有专业化和额外膨胀的单个函数中优雅地做到这一点?

编辑#2所以我解决这个问题的方法(这很明显,但不知何故让我逃脱了......)是定义一个bool ASSortable::operator<(const ASSortable& _o) const {return this->before(_o);};可以完成工作并使代码干净的方法(再次)。

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

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

4

5 回答 5

2

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

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有一个破解条件编译的实现细节,而不是接口的一部分。您不希望用户代码使用与您自己的类的类型不同的模板参数调用该成员函数。借用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>();

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

于 2012-07-12T14:06:11.543 回答
1

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

当前的 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参考文献:

视频:如果 Alexandrescu 在 Going Native 中呈现,则为静态。

n3322 : Walter E. Brown 的提议static if

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

于 2012-07-12T13:04:06.663 回答
1

预处理器在编译的早期运行,在编译器分析类型并知道它的含义之前std::is_fundamental<T>::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<T>您可以创建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());
}
于 2012-07-12T12:45:14.803 回答
0

std::is_fundamental<T>::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;
}
于 2012-07-12T12:48:48.623 回答
0

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

于 2012-07-12T12:45:51.317 回答