【问题标题】:Help with a template specialization帮助模板专业化
【发布时间】:2011-07-24 18:52:37
【问题描述】:

我正在使用/学习模板函数专业化规则。我从这个函数开始

template<typename T>
std::string toString(const T& t)
{
    ostringstream out;
    out << t;
    return out.str();
}

现在我想将它专门用于 const char*

typedef const char* ccharPtr;

template<>
std::string toString(const ccharPtr& s)
{
    cout << "in specialization" << endl; // just to let me know
    return std::string(s);
}

我想在没有 typedef 的情况下这样做,但到目前为止我无法弄清楚。

该特化适用于 const char*,但不适用于 char*。

const char* s1 = "Hi"
cout << toString(s1); // works
char s2[] = "There";
cout << toString(s2); // doesn't work, since s2 isn't const char*
cout << toString(", Bob"); // doesn't work. Why not?

我想要一个专长来处理每个案例,但很难搞清楚。

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    为什么要专业化?只是重载函数。通常不需要完全专业化的函数模板。

    template <typename T>
    std::string toString(const T& in)
    {
        ostringstream out;
        out << in;
        return out.str();
    }
    
    std::string toString(char const* in)
    {
        return in;
    }
    

    好的,如果你真的想要这样做,那么你必须考虑字符串文字的类型——尽管它们隐式地转换char const*——是@ 987654324@.

    template <typename T>
    std::string toString(T const & t) {
        ostringstream out;
        out << t;
        return out.str();
    }
    
    template <>
    std::string toString(char const* const & s) {
        cout << "(S1)";
        return std::string(s);
    }
    
    template <size_t N>
    std::string toString(char (&s)[N]) {
        cout << "(S2)";
        return std::string(s);
    }
    
    template <size_t N>
    std::string toString(char const (&s)[N]) {
        cout << "(S3)";
        return std::string(s);
    }
    
    int main() {
        const char* s1 = "Hi";
        cout << toString(s1) << endl;
        char s2[] = "There";
        cout << toString(s2) << endl;
        cout << toString(", Bob") << endl;
    }
    
    // Output:
    // (S1)Hi
    // (S2)There
    // (S3), Bob
    

    Live demo.

    您可以省略专门化S2,然后"There"", Bob" 都将使用S3

    请注意,实际上,这根本不是专业化。我宁愿通过创建 new 函数模板来作弊。但我必须得到size_t 参数;如果您为 N 选择 one 值并将其作为具体类型的一部分写入函数签名中,或者您可以 >部分专门化函数模板。

    【讨论】:

    • 传递一个字符串字面量,然后推导出一个与指针类型不匹配的数组类型(这是特化,所以类型必须完全匹配)。随着您显示的重载,它将使用第二个toString,因为转换序列将是模棱两可的,但第二个是非模板,因此获胜。
    • @Tomalak:我是否理解正确:您在T(第一个模板函数std::string toString(T const &amp; t))之后编写const,以使引用为const,在T 是指针的情况下?
    • @Christian:没错。对于专业化,论点必须匹配;所以我为T 选择了char const*,结果是char const* const&amp;,呈现出有效的专业化。
    【解决方案2】:

    它不起作用的原因是它实际上不是正确的类型。字符串文字不是const char* 类型,它们是const char[N] 类型。当你传递一个字符串字面量时,T 被推断为char[N]。这是完全匹配的,而专业化不是。您不能专门化字符串文字,因为这需要部分规范,而 C++ 不支持部分函数专门化。

    【讨论】:

      【解决方案3】:

      我认为这应该可行:

      template <typename T>
          std::string to_string(const T &)
      {
          ...
      }
      
      // non templated
      std::string to_string(const char* x)
      {
          ...
      }
      

      【讨论】:

      • 是的,它会起作用,但我想学习模板专业化。我知道,这是一个人为的案例。
      • 所以我猜你可能需要专门为 const char* 和 char* 专门一次。由于 char* 不是 const char*,我猜 C++ 不匹配专业化并使用更通用的函数。如果您希望 const char* 和 char* 都使用相同的专业化,我认为您必须明确地将参数从 char* 转换为 const char*。
      • @John:关于模板特化,您首先应该了解的一件事是函数模板特化很少有帮助。
      【解决方案4】:

      我在使用 Sun Studio C++ 编译器时遇到了类似的问题。

      起初,我使用了两个数组重载(char (&amp;)[N] 有和没有const)和两个指针重载(char*const char*)。

      令我惊讶的是,像toString("quoted literal") 这样的调用使用了const char*,而且似乎不可能强制编译器选择数组重载。更有趣的是——对于像char lit[] = "literal" 这样的参数,toString(lit) 选择了数组重载!

      经过一番思考,我找到了一种方法来编写一个表现为偏函数特化的代码。诀窍是利用 SFINAE

      首先,定义一个模板来区分(const) char*(const) char[N]

      template<typename T>
          struct is_literal {
      };
      
      template<size_t N>
      struct is_literal<const char[N]> {
          typedef std::string type;
          static const size_t len = N - 1;  // subtract terminating null char from N
      };
      
      template<size_t N>
      struct is_literal<char[N]> {
          typedef std::string type;
          static const size_t len = N - 1;
      };
      
      template<>
      struct is_literal<const char*> {
          typedef std::string ptr_type;
      };
      
      template<>
      struct is_literal<char*> {
          typedef std::string ptr_type;
      };
      

      内部 typedef type 作用类似于 enable_ifptr_type 类似于 disable_if

      我们可以使用is_literal来实现功能:

      template<typename T>
      typename is_literal<T>::type
      toString(T& arg)
      {
          std::cout << "(literal)";
          // note the second argument which means length
          // this can be a performance gain because no strlen call is needed
          return std::string(arg, is_literal<T>::len); 
      }
      
      template<typename T>
      typename is_literal<T>::ptr_type
      toString(T& arg)
      {
          std::cout << "(raw pointer)";
          return std::string(arg);
      }
      

      一些编译器可能会遇到const 的问题——因为可能需要const T&amp; arg 重载。

      当我经常调用函数并想保存strlen 调用或将strcpy 替换为简单的memcpy 时,我主要出于性能原因使用此解决方案

      【讨论】:

        猜你喜欢
        • 2020-07-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-28
        • 2015-08-01
        • 2011-09-23
        • 2014-07-06
        相关资源
        最近更新 更多