指针的 C++ 模板特化?

Posted

技术标签:

【中文标题】指针的 C++ 模板特化?【英文标题】:C++ template specialization for pointer? 【发布时间】:2017-01-19 09:07:58 【问题描述】:

我阅读了《C++ 模板 - 完整指南》一书并学习了指针的模板专业化。 (也许我误解了这本书的这一部分)

(1) 这是我的简单模板:

#include <iostream>

template<typename T>
void Function(const T& a)

    std::cout << "Function<T>: " << a << std::endl;


template<typename T>
void Function<T*>(const T* a)

    std::cout << "Function<T*>: " << a << std::endl;


int main(void)

    Function(1);
    Function(1.2);
    Function("hello");
    Function((void*)0x25);

    return 0;

我用的是ubuntu16.04 x64,g++ 5.3,编译报告:

$ g++ main.cpp -o main.exe 
main.cpp:10:29: error: non-type partial specialization ‘Function<T*>’ is not allowed
 void Function<T*>(const T* a)

(2) 但是这段代码是正确的:

#include <iostream>

template<typename T>
void Function(const T& a)

    std::cout << "Function<T>: " << a << std::endl;


int main(void)

    Function(1);
    Function(1.2);
    Function("hello");
    Function((void*)0x25);

    return 0;

结果显示:

$ g++ main.cpp -o main.exe
$ ./main.exe 
Function<T>: 1
Function<T>: 1.2
Function<T>: hello
Function<T>: 0x25

我的问题是:关于指针专业化的书是错的吗?还是我误解了书中这部分的含义?还是别的什么?

关于类中指针特化的更新。

(3) 带有指针特化的模板类:

#include <iostream>

template<typename T>
struct Base 
    T member;

    Base(const T& a)
        : member(a)
    
    

    void hello()
    
        std::cout << member << std::endl;
    
;

template<typename T>
struct Base<T*> 
    T* member;

    Base(T* a)
        : member(a)
    
    

    void hello()
    
        std::cout << member << std::endl;
    
;

int main(void)

    Base<int> b1(12);
    Base<double> b2(2.4);
    Base<char*> b3("hello");
    Base<void*> b4((void*)0x25);

    b1.hello();
    b2.hello();
    b3.hello();
    b4.hello();

    return 0;

此代码正确,但有一个警告:

$ g++ main.cpp -o main.exe 
main.cpp: In function ‘int main()’:
main.cpp:37:27: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
     Base<char*> b3("hello");
                           ^
$ ./main.exe 
12
2.4
hello
0x25

(4) 没有指针特化的模板类:

#include <iostream>

template<typename T>
struct Base 
    T member;

    Base(const T& a)
        : member(a)
    
    

    void hello()
    
        std::cout << member << std::endl;
    
;

int main(void)

    Base<int> b1(12);
    Base<double> b2(2.4);
    Base<char*> b3("hello");
    Base<void*> b4((void*)0x25);

    b1.hello();
    b2.hello();
    b3.hello();
    b4.hello();

    return 0;

结果是一样的:

$ g++ main.cpp -o main.exe
main.cpp: In function ‘int main()’:
main.cpp:39:27: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
     Base<char*> b3("hello");
                           ^
$ ./main.exe 
12
2.4
hello
0x25

这是否意味着不需要指针特化? 或者也许这个特性在不同的编译器上表现不同?

【问题讨论】:

什么是测试?不是到处都用吗?为什么同一个函数要声明两次?什么是编译器错误?请只发布相关代码。 通常答案在错误信息中。你看了吗? 我用 clang 得到的错误是 error: function template partial specialization is not allowed。 GCC 报告类似。那应该回答你的问题。 【参考方案1】:

正如您已经被告知的,函数模板的部分特化是不允许的。您可以为此使用std::enable_if

template <typename T, typename std::enable_if_t<!std::is_pointer<T>::value>* = 0>
void func(T val)  std::cout << val << std::endl; 

template <typename T, typename std::enable_if_t<std::is_pointer<T>::value>* = 0>
void func(T val)  func(*val); 

如果您正在寻找更简单的语法,请等待概念

【讨论】:

你将如何处理概念? @Teharez 像这样:template &lt;typename T&gt; requires !std::is_pointer_v&lt;T&gt; void func(T val) std::cout &lt;&lt; val &lt;&lt; std::endl; 和相应的第二次重载。您也可以将requires 作为函数限定符或使用指针的一些概念【参考方案2】:

错误信息告诉你出了什么问题:

non-type partial specialization ‘Function<T*>’ is not allowed

您只能部分专门化类型(类)。您已尝试部分专门化一个函数。函数不是类型;你只能完全专业化它们。

【讨论】:

哦,是的,我错了。【参考方案3】:

两个问题:

    不允许对函数进行部分特化。

    (void*)0x25 的行为未定义。除了nullptr,你不能设置一个指向你不拥有的内存的指针,除了一个超过数组的最后一个元素和一个超过标量的地址。

【讨论】:

(void*)0x25 不是书上的,是我的错。

以上是关于指针的 C++ 模板特化?的主要内容,如果未能解决你的问题,请参考以下文章

C++模板的特化与偏特化

C++模板详解:泛型编程模板原理非类型模板参数模板特化分离编译

C++模板详解:泛型编程模板原理非类型模板参数模板特化分离编译

C++模板编程中只特化模板类的一个成员函数(花样特化一个成员函数)

C++ template —— 模板特化

C++初阶:模板进阶非类型模板参数 | 模板的特化 | 模板分离编译