【问题标题】:How to prevent specialization of a C++ template?如何防止 C++ 模板的特化?
【发布时间】:2015-01-05 19:47:13
【问题描述】:

当我这样做时编译器不会抱怨 ;-)

// Myfile.h
#include <iostream>
#include <vector>

namespace std
{

template<> class vector<int>
{
public:
    vector ()
    {
        std::cout << "Happy Halloween !!!\n";
    }
};

}

有什么方法可以防止类/函数模板的这种不受欢迎的特化?

--编辑--

我只是以 std:: 为例。我正在寻找的是一种防止这种情况发生在任何模板类上的方法。

【问题讨论】:

  • @nav 编译器没有发出警告或错误并不意味着可以做某事。
  • @LuchianGrigore 我同意这种情况。 OP的代码显然是荒谬的。但是一个可以特化一个std模板,只要它特化依赖于用户定义的类型,特化满足原始模板行为的要求,并且模板不被禁止特化。标准。最后我还是检查了。这不是我第一次误读标准。
  • 为什么要阻止它?你显然不能阻止每一种虚假的用例——例如。人们也可以抛弃 const,你不能在那里阻止他们——那么你为什么要特别关注这个特殊情况呢?
  • 阻止人们做蠢事往往是以阻止他们做好事为代价的。有不同的方法,但我个人更担心的是记录模板所依赖的内容(以便人们可以正确使用它们),而不是试图想象人们可能会尝试哪些坏事。
  • 防止模板特化的唯一有效方法如下:// Don't specialize this template, or else. -- The boss

标签: c++ templates c++11 namespaces


【解决方案1】:

您所做的是在标准命名空间中专门化标准库类型。

除了一些记录在案的自定义点(std::swap、std::hash)或用户定义类型的特别受限的特化(例如MySmartPtr&lt;T&gt;),这违反了规范并且结果是未定义的行为。


编辑:这种违规行为没有强制诊断。

为了让你的图书馆的客户更难搞砸,你可以做这个技巧:

namespace Public {


    namespace Hidden { // DON'T TOUCH THESE!
        template <typename> struct MyType { };
    }

    using Hidden::MyType;

}

现在,尝试在命名空间 Hidden 中专门化 MyType&lt;&gt; 将失败。

【讨论】:

  • 谢谢sehe.. 但是我写的课怎么样?有人搞砸了有什么办法吗? (请参阅我对问题的编辑)
  • @nav 添加了一个让它变得更难的想法。但是,您不能 "protect against Machiavelli"(粗略地引用 Herb Sutter)
  • 大多数标准库模板可以专门用于用户定义的类型([namespace.std]/p1:“程序可以将任何标准库模板的模板专门化添加到命名空间 std 只有当声明依赖在用户定义的类型上,并且特化符合原始模板的标准库要求并且没有明确禁止。”),尽管只有少数通常以这种方式特化。
  • Heretemplatestd 中的专业化规则的粗略列表。乍一看,这似乎是合理的。一般规则是“允许,但除外”,这列出了额外的要求。请注意,OP 的专业化是非法的,原因有两个(int,由此产生的vector 未能通过vector 的要求)
  • +1 用于命名空间技术。
【解决方案2】:

不,C++ 语言不提供通用机制,您可以通过该机制说“不允许此模板的特化”。

但这可能无关紧要。对于您的代码已经使用的任何实例化,用户提供的专业化将违反单一定义规则,并且他们的程序可能会像火球一样爆炸。

如果你在你的库中使用实例化,那么他们做什么并不重要。

这是在 C++ 中您根本无法阻止用户朝自己的脚开枪的情况之一,如果他们选择这样做,责任就在他们身上。

【讨论】:

    【解决方案3】:

    alias template 不能专门化,并且具有类模板所需的行为。

    template<class T>
    struct my_class_implementation_which_should_not_be_used_directly
    {
        // impl
    };
    
    
    template<class T>
    using my_class = my_class_implementation_which_should_not_be_used_directly<T>;
    

    此外,您应该记录专门化my_class_implementation_which_should_not_be_used_directly 会导致未定义的行为。现在您的库用户不能意外地专门化 my_class 并直接警告带有丑陋名称的类。

    【讨论】:

    • 接受这个明确提及“别名模板”
    【解决方案4】:

    编辑:

    您可以在 C++11 中使用 enable_if 防止模板的特化。但这是不切实际的。

    #include <type_traits>
    
    template<
      typename real_t,
      typename = typename std::enable_if<
        std::is_floating_point<real_t>::value
      >::type
    >
    struct check_t;
    
    template<
      typename real_t,
      typename = typename
        std::enable_if<
          std::is_floating_point<real_t>::value,
          check_t<real_t>
        >::type
    >
    class vec_t
    {
    
    };
    
    #if 1
    template<>
    class vec_t<int> {};
    
    template<>
    class vec_t<int,check_t<int>> {};
    #endif
    
    void test()
    {
        vec_t<float> vecf;
        vec_t<int> veci;
    }
    

    link

    main.cpp:26:16: error: no type named 'type' in 'struct std::enable_if<false, void>'
    

    【讨论】:

    • 不,如果提供的类型不是浮点数,这会阻止任何实例化。我仍然可以专攻它,例如code example.
    【解决方案5】:

    防止此类行为的最佳方法是通过编码标准和代码审查。

    在这种情况下,您不能强制编译器出错(除了使用其他答案中建议的解决方法),因为该行为实际上是语言允许的,尽管它的真正用处可能会受到质疑。 问题是 - 您很可能希望/需要为多种类型提供通用行为(因此使用模板),但您需要为某些允许的类型(特别是 std::string)提供特定的实现

    一个快速而肮脏的例子(它是一个函数,但同样适用于类)可能如下:

    template<typename TData> TData GetData(std::string argument)
    {
        std::stringstream stream;
        TData retVal;
        stream.str(argument);
        stream >> retVal;
        return retVal;
    }
    

    然而,这会因 std::string 而失败,因为 >> 运算符会在第一个空格之后停止。所以你可以提供一个专门的专业。

    template<> std::string GetData(std::string argument)
    {
        return argument;
    }
    

    【讨论】:

      【解决方案6】:

      有几种方法可以解决这个问题。您可以声明特定的特化而不定义它们。例如:

      template<> struct MyStruct<int>;
      

      这样,如果有人试图用一个 int 实例化,他们会得到这个特化,它不会编译,因为有 n 个定义。然后,您可以编写一个简单的宏来为您不希望专门化的所有类型执行此操作。

      反之,定义为空:

      template<typename T> struct MyStruct{};
      

      然后定义你计划支持的具体类型:

      template<> struct MyStruct<int> {...};
      template<> struct MyStruct<std::string> {...};
      //etc...
      

      【讨论】:

      • 如果有人想对int 使用普通类(没有专门化),那么它也不会编译。而且他应该申报多少专业?
      • 你回答的前提是错误的。 template&lt;&gt; struct MyStruct&lt;int&gt;; 只是一个声明。仍然没有什么阻止其他开发人员为其提供定义。
      • @iammilind 这就是为什么我提供了两种不同的解决方案 - 一种是另一种的否定/相反。
      • @CaptainObvlious 是的,你是对的。我想我应该说的是提供一个空定义,以便由于 ODR 无法再次定义它,并且当模板类的客户端尝试实际引用已定义的成员时,它也无法在实际使用中工作在默认或提供的专业化中。
      猜你喜欢
      • 2023-03-09
      • 2018-05-19
      • 1970-01-01
      • 2022-07-05
      • 1970-01-01
      • 2011-10-27
      • 1970-01-01
      • 2021-11-04
      • 2021-05-29
      相关资源
      最近更新 更多