【问题标题】:What is the best way to create a specialization-only function template?创建仅专业化功能模板的最佳方法是什么?
【发布时间】:2012-01-08 11:24:11
【问题描述】:

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

#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&lt;float&gt;() 的引用”等。这可能会让其他开发人员感到困惑,因为他们可能怀疑没有链接实现文件。

我确实知道另一个潜在的解决方案:

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

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

总之,我想知道是否:

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

【问题讨论】:

  • BOOST_STATIC_ASSERT(false); 更直接地说出你想要什么。

标签: c++ templates boost template-specialization


【解决方案1】:

这可以工作:

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 -- 但这对于某人在编写类模板时使用来说似乎是一个合理的名称。也许把它全大写以减少这种可能性。
  • 是的,不理想,但我喜欢这样。
  • 不确定您所说的“可能的最大尺寸”是什么意思,因为您使用 sizeof(T) == -1,但 sizeof(T) == numeric_limits<:size_t>::max是一个有趣的想法。
  • @Mozza314:因为sizeof的类型是std::size_t(无符号整数类型),-1会被提升为无符号类型(使用模运算,保证最大值)。也就是说,只需使用零。在某些时候,您必须相信编译器正在做正确的事情。
  • 是的,我在您编辑答案 GMan 之前写了该评论。但是, std::size_t(-1) 是未定义的,不是吗?当然,即使实际上用二进制补码说话,你也会得到最大值。
【解决方案2】:

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);

这既保证失败,编译器也不可能提前正确“智能”失败。

【讨论】:

  • 你能引用标准吗?我的怀疑是,如果编译器可以告诉没有模板参数可以通过,则允许编译器给出错误。来自 boost 邮件列表的这篇文章似乎表明了这一点:lists.boost.org/Archives/boost/2004/06/66983.php
  • @Mozza314:我正在编辑我的答案,我确定海报是错误的。给我一分钟。
  • 你太棒了:-D。谢谢。
  • "BOOST_STATIC_ASSERT(sizeof(T) == 0); 是依赖的,在模板实例化之前不允许检查。(它总是会失败。)“这是错误的。该标准允许编译器拒绝在实例化时总是失败的模板。如果编译器试图考虑周到并提前失败,那是聪明的。如果你想写 o 而不是 0 怎么办 - 聪明的编译器会在你进入混乱的实例化回溯之前向你显示你的错字。
  • @JohannesSchaub-litb:我认为不允许这样做,有什么地方可以阅读吗?
【解决方案3】:

您可以执行以下操作:

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

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

【讨论】:

    【解决方案4】:

    在 c++0x 中使用 static_assert

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

    这将在编译时引发错误。

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

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

    数组至少包含一个元素。所以编译器会抱怨。但错误消息可能无法显示发生了什么。

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

    【讨论】:

    • First 不正确,因为它总是会失败。在没有 -pedantic 的 GCC 下,第二个编译得很好。
    • 有趣。我猜它并不比 BOOST_STATIC_ASSERT(sizeof(T) == 0) 好。这个解决方案也有同样的问题。
    • Pubby 的评论在我之前通过。是的 -pedantic 即使没有实例化也会失败。
    【解决方案5】:

    如果您在没有 -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 编译

    【讨论】:

      猜你喜欢
      • 2020-06-26
      • 1970-01-01
      • 2018-04-07
      • 1970-01-01
      • 2011-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-19
      相关资源
      最近更新 更多