创建仅限专业化功能模板的最佳方法是什么?

时间:2011-11-29 03:15:57

标签: c++ templates boost template-specialization

有更好的方法来执行以下操作吗?

#include <iostream>

template <typename T>
T Bar();

template <>
int Bar<int>() { return 3; }

// Potentially other specialisations

int main()
{
    std::cout << Bar<int>() << std::endl; // This should work
    std::cout << Bar<float>() << std::endl; // This should fail
}

这个解决方案的问题在于它(可理解地)链接时间与“未定义引用浮动Bar<float>()”等失败。这可能会让其他开发人员感到困惑,因为他们可能怀疑实现文件没有被链接。

我知道另一种可能的解决方案:

template <typename T>
T Bar() { BOOST_STATIC_ASSERT(sizeof(T) == 0); }

这会在请求Bar<float>()时导致编译器错误,这正是我想要的。但是,我担心技术上编译器可能会拒绝这一点,就像gcc拒绝BOOST_STATIC_ASSERT(false)一样,因为它知道无论模板参数如何都会失败,因为sizeof(T) 永远不会为零。

总之,我想知道是否:

  1. 还有另一种方法可以做到这一点。
  2. 我错了,BOOST_STATIC_ASSERT(sizeof(T))实际上没有实例化就失败了。
  3. 唯一的办法就是让它成为上面的链接器错误。

5 个答案:

答案 0 :(得分:6)

这可行:

template <typename T>
T Bar() {
  T::ERROR_invalid_template_argument_;
}

template <>
int Bar<int>() { return 3; }

如果您害怕使用0:

,您也可以使用最大尺寸
  static_assert(sizeof(T) == -1, "No specialization");

答案 1 :(得分:4)

在模板实例化之前,不允许

BOOST_STATIC_ASSERT(sizeof(T) == 0);失败,所以我只会这样做。你是正确的BOOST_STATIC_ASSERT(false);每次触发。


这个原因与两阶段名称查找有关。这基本上是以下内容:编译模板时,它被编译两次。 compielr第一次看到模板时,它会编译所有,除了依赖于模板参数的表达式,第二次编译在模板参数已知时发生,完全编译实例化。

这就是为什么BOOST_STATIC_ASSERT(false);将永远失败的原因:此处没有任何依赖,并且会立即处理断言,就好像该函数根本不是模板一样。 (请注意,MSVC不实现两阶段查找,因此在实例化时会失败,这是错误的。)相反,因为T是依赖的(§14.6.2.1),BOOST_STATIC_ASSERT(sizeof(T) == 0);是依赖的,并且是在模板实例化之前不允许检查。 (如果它永远都会失败。)

如果编译器试图考虑周到并且提前失败,那么它将是不符合的。你可以依靠这些东西。也就是说,如果恐惧变得最好,那么真的让它等待是微不足道的:

BOOST_STATIC_ASSERT(sizeof(typename T::please_use_specializations) == 0);

这两者都保证失败,并且编译器无法提前正确“智能地”失败。

答案 2 :(得分:0)

您可以执行以下操作:

template <typename T>
T Bar()
{ T::unspecialized_method_called; }

这当然假设T没有具有给定名称的成员,因此您必须相应地选择“错误消息”(例如,违反命名约定)。

答案 3 :(得分:0)

将static_assert与c ++ 0x

一起使用
template <typename T> 
void bar(){
 static_assert(false, " invalid argument type");
}

这会在编译时引发错误。

对于c ++ 98/2003,我们可以试试这个

template <typename T> 
void bar(){
char invalid_arg_[0];
}

数组至少包含一个元素。所以编译器会抱怨。但错误信息可能无法显示发生的事情。

第一个不会是一个选择,因为它总是失败。

答案 4 :(得分:0)

如果您使用的是没有-pedantic的gcc,有一点需要注意,在这种情况下 可能有sizeof(T) == 0 - 当T是零长度数组时

#include <iostream>

#include "boost/static_assert.hpp"

template <typename T>
void Foo()
{
    BOOST_STATIC_ASSERT(sizeof(T) == 0);
    std::cout << "Actually, it is possible to instantiate this." << std::endl;
}

int main()
{
    Foo<int[0]>();

    return 0;
}

在这种情况下,你可以通过使用它来解决它:

BOOST_STATIC_ASSERT(sizeof(T) == sizeof(T) + 1);

封装这个技巧可能会更好,这可以提高可读性,因为它表达了你的意图:

#define NEVER_INSTANTIATE(T) BOOST_STATIC_ASSERT(sizeof(T) == sizeof(T) + 1);

正如GMan解释的那样,如果没有sizeof(T) == 0的实例化,这不会失败。但是,这个故事的寓意应该是总是用-pedantic 编译。