【问题标题】:"multiple definition of..." error for a full specialisation of a template function模板函数完全专业化的“...的多重定义”错误
【发布时间】:2010-12-20 23:13:19
【问题描述】:


我有一个配置项目:

./main.cpp  
./type_traints/TypeTraints.cpp
./type_traints/TypeTraints.hpp
./type_traints/chapter_20.hpp

./type_traints/CMakeLists.txt 文件是:

 cmake_minimum_required (VERSION 2.8)
 add_library(chapter_20 TypeTraints.cpp)

./CMakeLists.txt 如下:

cmake_minimum_required (VERSION 2.8)
project (mpl)

add_subdirectory(type_traints)
include_directories(type_traints)
link_directories(type_traints)

add_executable (mpl main.cpp)
target_link_libraries(mpl chapter_20)

文件的相关部分(大部分包括省略)包括:
./type_traints/chapter_20.hpp

#ifndef CHAPTER_20_GUARD
#define CHAPTER_20_GUARD
#include <TypeTraints.hpp>

void chapter_20() {
  test_23();
}   
#endif //CHAPTER_20_GUARD

./type_traints/TypeTraints.hpp

#ifndef TYPE_TRAINTS_GUARD
#define TYPE_TRAINTS_GUARD
namespace details {

  template<class T> const char* class2name() {
    return "unknown";
  };

  template<> const char* class2name<int>() {
    return "int";
  };
}

template<class T>
class type_descriptor {
  friend std::ostream& operator << (std::ostream& stream, 
                                    const type_descriptor<T>& desc) {
    stream << desc.getName();
    return stream;
  }

public:
  std::string getName() const;  
};

template<class T>
std::string type_descriptor<T>::getName() const {
  return details::class2name<T>();
}

void test_23();    
#endif // TYPE_TRAINTS_GUARD

./type_traints/TypeTraints.cpp

#include<TypeTraints.hpp>

void test_23() {
  cout << type_descriptor<int>() << endl;
}

和./main.cpp

#include <chapter_20.hpp>

int main(int argc, char* argv[]) {
chapter_20();
  return 0;
}

项目编译但链接失败:

[ 50%] Building CXX object type_traints/CMakeFiles/chapter_20.dir/TypeTraints.cpp.o
Linking CXX static library libchapter_20.a
[ 50%] Built target chapter_20
[100%] Building CXX object CMakeFiles/mpl.dir/main.cpp.o
Linking CXX executable mpl
type_traints/libchapter_20.a(TypeTraints.cpp.o): In function `char const* details::cl
ass2name<int>()':                                                                   
/home/marcin/Projects/mpl/type_traints/TypeTraints.hpp:312: multiple definition of `c
har const* details::class2name<int>()'                                              
CMakeFiles/mpl.dir/main.cpp.o:/home/marcin/Projects/mpl/type_traints/TypeTraints.hpp:
312: first defined here                                                             
collect2: ld returned 1 exit status
make[2]: *** [mpl] Błąd 1
make[1]: *** [CMakeFiles/mpl.dir/all] Error 2
make: *** [all] Error 2
23:56:20@marcin-laptop ~/P

如果我从 TypeTraints.hpp 中删除 class2name 特化 (class2name&lt;int&gt;()) 并仅使用通用实现,则项目链接正常。

有人知道为什么会这样吗?我是否错过了配置 cmake 文件?

【问题讨论】:

    标签: c++ templates definition


    【解决方案1】:

    简而言之:显式(即完全)专用的模板函数不再是模板。它是一个普通函数,它遵循普通函数的单一定义规则。

    换句话说,您不能在头文件中明确定义专门的函数模板。这样做会导致违反 ODR。

    一个模板是一个模板,只要它依赖于至少一个参数。 IE。 部分特化可以在头文件中定义(因为它们仍然是模板)。 显式(即完整)特化只能在头文件中声明,但必须在实现文件中定义,就像普通函数一样。

    【讨论】:

    • 感谢您的回答。但是,在 TypeTraints.hpp 中将函数定义更改为声明 (template const char* class2name();) 并在 TypeTraints.cpp 中添加定义 (template const char* class2name() {return " int"} 产生一个编译错误:"/home/marcin/Projects/mpl/type_traints/TypeTraints.cpp:120: error: expected initializer before '
    • @Marcin:你搞砸了。没有看到新代码就无法判断。我猜你忘记了命名空间。定义应该在 details 命名空间内完成,或者应该使用限定名称(即它必须明确声明它是 details::class2name&lt;int&gt; 您正在定义)。
    • 是的,我搞砸了命名空间。一个久经考验的程序员是一个愚蠢的程序员:/谢谢你太棒了!
    【解决方案2】:

    这违反了单一定义规则:

    可以有多个定义 类类型(第 9 条), 枚举类型 (7.2),内联 外联功能 (7.1.2),类模板(第 14 条), 非静态函数模板(14.5.5), 类模板的静态数据成员 (14.5.1.3),类的成员函数 模板 (14.5.1.1) 或 模板 一些模板的专业化 未指定参数 (14.7, 14.5.4) 在程序中,前提是每个定义以不同的形式出现 翻译单位,并提供 定义满足以下 要求。

    class2name 模板函数的显式特化不属于这些情况。出于这个原因,我相信将class2name&lt;int&gt;() 定义移动到实现文件中应该可以解决这个问题。我也觉得你应该看看"Why not specialize function templates ?"

    【讨论】:

    • 我知道模板函数专业化的缺点——这只是一个教育练习
    【解决方案3】:

    您的文件:

    ./main.cpp  
    ./type_traints/TypeTraints.cpp  
    ./type_traints/TypeTraints.hpp  
    ./type_traints/chapter_20.hpp
    

    其中TypeTraints.hpp直接包含在TypeTraits.cpp中,间接包含在main.cpp中(通过chapter_20.hpp)。但是,您完全规范模板

     template<> const char* class2name<int>() {
         return "int";
     };
    

    在 TypeTraints.hpp 中定义,它们存在于两个不同的编译单元中(上述两个 .cpp 文件)。这两个文件将在编译后链接在一起,这会导致多定义链接错误。

    【讨论】:

    • 只是想知道,为什么在这种情况下包含保护不能防止多个定义?
    猜你喜欢
    • 1970-01-01
    • 2021-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 2011-01-18
    • 1970-01-01
    相关资源
    最近更新 更多