创建专门化函数模板的最佳方法是什么?

What is the best way to create a specialization-only function template?

本文关键字:方法 是什么 最佳 专门化 函数模板 创建      更新时间:2023-10-16

是否有更好的方法来做以下事情?

#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. 唯一的方法就是像上面那样让它是一个链接错误。

可以这样做:

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

如果你害怕使用0:

,你也可以使用最大可能的大小。
  static_assert(sizeof(T) == -1, "No specialization");

BOOST_STATIC_ASSERT(sizeof(T) == 0);在模板实例化之前是不允许失败的,所以我只做那个。你是正确的,BOOST_STATIC_ASSERT(false);每次触发。


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

这就是为什么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);

这既保证会失败,也不可能让编译器正确地"聪明地"提前失败。

您可以这样做:

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

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

using static_assert with c++0x

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

this将在编译时引发错误。

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

template <typename T> 
void bar(){
char invalid_arg_[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来编译。