【问题标题】:C++ template specialization without default function没有默认函数的 C++ 模板特化
【发布时间】:2009-10-27 08:25:11
【问题描述】:

我有以下代码可以编译并且运行良好:

template<typename T>
T GetGlobal(const char *name);

template<>
int GetGlobal<int>(const char *name);

template<>
double GetGlobal<double>(const char *name);

但是我想删除“默认”功能。也就是说,我想对 GetGlobal 进行所有调用,其中 't' 不是 int 或 double 错误。

例如 GetGlobal() 应该是编译时错误。

我试图只删除默认函数,但正如我想象的那样,我收到了很多错误。那么有没有办法“禁用”它并只允许调用该函数的专用版本?

谢谢!

【问题讨论】:

    标签: c++ templates specialization


    【解决方案1】:

    虽然这是一个古老而过时的问题,但可能值得注意的是C++11 已使用已删除的函数解决了此问题:

    template<typename T>
    T GetGlobal(const char *name) = delete;
    
    template<>
    int GetGlobal<int>(const char *name);
    

    更新

    这不会在MacOS llvm 8 下编译。 这是由于一个 4 年前的缺陷仍然悬而未决(请参阅 this bug report)。

    以下解决方法将解决该问题(使用static_assert 构造)。

    template<typename T>
    T GetGlobal(const char *name) {
        static_assert(sizeof(T) == 0, "Only specializations of GetGlobal can be used");
    }
    
    template<>
    int GetGlobal<int>(const char *name);
    

    更新

    Visual Studio 15.9 也有同样的错误。使用之前的解决方法。

    【讨论】:

    【解决方案2】:

    要获得编译时错误,请将其实现为:

    template<typename T>
    T GetGlobal(const char *name) { T::unimplemented_function; }
    // `unimplemented_function` identifier should be undefined
    

    如果你使用 Boost,你可以让它更优雅:

    template<typename T>
    T GetGlobal(const char *name) { BOOST_STATIC_ASSERT(sizeof(T) == 0); }
    

    C++ 标准保证不存在 sizeof 等于 0 的类型,因此您会收到编译时错误。

    正如sbi 在他的 cmets 中建议的那样,最后一个可以简化为:

    template<typename T>
    T GetGlobal(const char *name) { char X[!sizeof(T)]; }
    

    我更喜欢第一个解决方案,因为它比其他解决方案提供更清晰的错误消息(至少在 Visual C++ 中)。

    【讨论】:

    • 为了让第一个案例在编译时失败,Koper 需要一个未声明函数,而不是未定义 i> 一。编译它的唯一方法是让它依赖于T。像T::some_thing_that_is_definitely_undeclared 这样的东西可能会做。 (第二个可能会被char dummy[!sizeof(T)]; 模仿。)
    • 使用unimplemented_function 提供比使用T::unimplemented_function 更清晰的错误信息(至少在Visual C++ 中)。
    • @Kirill:使用unimplemented_function 在执行两阶段查找的编译器中不起作用。 (目前,VC 不这样做。)这样的编译器不会编译模板 definition,即使它从未实例化。
    • -1:SBI 是正确的。查找标识符,因为它不是带有依赖参数的函数调用(即 ADL 调用),所以它将是一个错误。请修正这个例子 - 并收回你的观点! :)
    • 这样的template&lt;typename T&gt; struct not_defined : mpl::false_ { }; template&lt;typename T&gt; T GetGlobal(char const *name) { BOOST_MPL_ASSERT(( not_defined&lt;T&gt; )); } 会给出一些不错的错误:codepad.org/rq30wdeq
    【解决方案3】:

    如果你不实现它,你至少会得到一个链接器错误。如果你想要一个编译时错误,你可以使用类模板来做到这一点:

    template<typename T>
    struct GlobalGetter;
    
    template<>
    struct GlobalGetter<int> {
      static int GetGlobal(const char *name);
    };
    
    template<>
    struct GlobalGetter<double> {
      static double GetGlobal(const char *name);
    };
    
    template<typename T>
    T GetGlobal(const char *name)
    {
      return GlobalGetter<T>::GetGlobal(name);
    }
    

    【讨论】:

    • 知道为什么模板函数前向声明不会产生编译器错误,而模板类前向声明​​会产生错误吗?
    • 如果你有比'int'和'double'更多的专业,那就写太多了。
    • 什么是GlobalGetter&lt;T&gt;(name)?构造函数? GlobalGetter&lt;int&gt;GlobalGetter&lt;double&gt; 中没有单参数的构造函数。
    • @xtofl:因为函数模板声明是编译函数调用所需的全部内容。 (而对于你需要定义的类。)
    • @Kiril:您无需编写函数模板特化,而是为仅包含一个统计函数的类模板编写特化。是的,要键入更多的按键,但这还不错。但是,您的第二个解决方案(我也喜欢使用 STATIC_ASSRT 的那个。
    【解决方案4】:

    我建议不要实际提供实现,只是简单地声明方法。

    另一种选择是使用编译时断言。 Boost 有很多这样的野兽。

    namespace mpl = boost::mpl;
    BOOST_MPL_ASSERT((mpl::or_< boost::same_type<T, double>,
                                boost::same_type<T, int> >));
    

    还有对应的消息版本,这会有所帮助。

    【讨论】:

      【解决方案5】:

      以下是使用 boost 的替代技术:

      为依赖名称声明 typedef

      这是有效的,因为 DONT 的名称查找仅在“T”被替换时发生。这是Kirill给出的示例的类似(但合法)版本

      template <typename T>
      T GetGlobal (const char * name) {
          typedef typename T::DONT CALL_THIS_FUNCTION;
      }
      

      使用不完整的返回类型

      此技术不适用于特化,但适用于重载。这个想法是声明一个返回不完整类型的函数是合法的,但不能调用它:

      template <typename T>
      class DONT_CALL_THIS_FUNCTION GetGlobal (const char * name);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-05-18
        • 2023-03-20
        • 1970-01-01
        • 1970-01-01
        • 2013-09-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多