MSVC DLL exporting class that inherits from template cause LNK2005 already defined error

Viewed 941

I've been tracking an error in a huge project for 3 days and finally get a minimum reproducable example. I want to share this problem and ask some questions on the weird behavior of Visual Studio.

When you export a class that inherits from a instantiated template class , like

class __declspec(dllexport) classA : public Template<double>{}

MSVC will also export the instantiated template class Template<double> in DLL.

If the consumer include "template.h" in his code then instantiate a Template<double>, qnd at the same time, link to the above DLL, he will get two definition of Template<double>, which causes LNK2005 and LNK1169 error. This problem is discussed in Microsoft DLL export and C++ templates.

Here is a minimum reproducable example of this problem (Full code is here):

// ----------- Project AA ------------
// aa/CMakeLists.txt
cmake_minimum_required(VERSION 3.1)
project(AA)
add_library(AA SHARED classA.cpp)                          //(1)
set_target_properties(AA PROPERTIES COMPILE_FLAGS "-DBUILD_AA")

// aa/config.h
#pragma once

#ifdef _WIN32
#  ifdef BUILD_AA
#    define AA_API __declspec( dllexport )
#  else
#    define AA_API __declspec( dllimport )
#  endif
#else
#   define AA_API
#endif

// aa/template.h
#pragma once
template<class T>
class Template {
public:
    Template() {}
};

//  aa/classA.h
#pragma once
#include "config.h"
#include "template.h"
class AA_API classA : public Template<double> {         //(2)
public:
    int fun();
};

// aa/classA.cpp
#include "classA.h"
int classA::funA(){return 123;}

// ----------- Project Main ----------
//CMakeLists.txt
cmake_minimum_required(VERSION 3.1)
project(Main)
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
add_subdirectory(aa)
add_executable(Main main.cpp test.cpp)
target_link_libraries(Main PUBLIC AA)

// main.cpp
#include "aa/classA.h"
#include <iostream>
int main(){
    Template<double> a;                                //(3)
    //classA aa;                                       //(4)
    //std::cout << aa.funA() << std::endl;             //(5)
    return 0;
}

// test.cpp
#include "aa/template.h"
class classB {
    Template<double> t;
};
class classC : public Template<double> {};

void fun() {
    Template<double> b;                               //(6)
    //class classB;                                   //(7)
    //class classC;                                   //(8)
}

I compile the code in VS2015 Debug mode, it gives multiply defined error

1>------ Build started: Project: AA, Configuration: Debug x64 ------
1>  classA.cpp
1>  AA.vcxproj -> D:\sandbox\build\aa\Debug\AA.dll
2>------ Build started: Project: Main, Configuration: Debug x64 ------
2>  main.cpp
2>AA.lib(AA.dll) : error LNK2005: "public: __cdecl Template<double>::Template<double>(void)" (??0?$Template@N@@QEAA@XZ) already defined in test.obj
2>D:\sandbox\build\Debug\Main.exe : fatal error LNK1169: one or more multiply defined symbols found
========== Build: 1 succeeded, 1 failed, 1 up-to-date, 0 skipped ==========

If I do ONE OF the following changes, there will be no error:

  1. Change to Release mode
  2. Remove AA_API at (2) and change link mode to STATIC at (1)
  3. Comment (3) and uncomment (4) and (5)
  4. Comment (6) and uncomment (7) and (8)
  5. Compile in Linux by gcc
  6. Change project parameter in VS : add /FORCE:MULTIPLE to Linker for project Main

Question:

Why changes 1,2,3,4 and 5 work ? I think it's so weird that 3 and 4 can work.

How to solve this problem properly in Visual Studio ?

1 Answers
Related