2017-07-07 2 views
0

Ich habe einen Fehler in einem großen Projekt für 3 Tage verfolgt und schließlich ein Minimum reproduzierbar Beispiel erhalten. Ich möchte dieses Problem teilen und einige Fragen zum seltsamen Verhalten von Visual Studio stellen.MSVC DLL exportierende Klasse, die von Vorlage erbt Ursache LNK2005 bereits definierten Fehler

Wenn Sie eine Klasse exportieren, die von einer instanziierten Template-Klasse erbt, wie

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

MSVC wird exportiert auch die instanziiert Template-Klasse Template<double> in DLL.

Wenn die Verbraucher "template.h" in seinem Code enthalten dann ein Template<double>, instanziiert zugleich, Link zu dem oben DLL QND, wird er zwei Definition von Template<double>, erhält die LNK2005 und LNK1169 Fehler verursacht. Dieses Problem wird in Microsoft DLL export and C++ templates diskutiert.

ist hier ein Minimum reproduzierbares Beispiel für dieses Problem (Full-Code ist 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) 
} 

ich den Code in VS2015 Debug-Modus kompilieren, gibt sie mehrfach Fehler definiert

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)" ([email protected]@@[email protected]) 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 ========== 

Wenn ich tun ONE OF die folgenden Änderungen, wird es keinen Fehler geben:

  1. Änderungsmodus bei (1)
  2. Kommentar (3) und uncomment (4) und (5)
  3. Kommentar (6) und uncomment (7) und (8)
  4. Compile in Linux von gcc
  5. entfernen AA_API bei (2) und ändern Link-Modus STATIC Release
  6. Projektparameter in VS ändern: /FORCE:MULTIPLE zum Linker für Projekt Main
  7. hinzufügen

Frage:

Warum Änderungen 1,2,3,4 und 5 arbeiten? Ich finde es komisch, dass 3 und 4 funktionieren können.

Wie kann dieses Problem in Visual Studio ordnungsgemäß gelöst werden?

Antwort

1

Ich habe in letzter Zeit das gleiche Problem festgestellt. Ich habe es geschafft, es endlich zu beheben, damit ich mein Wissen teile.

Quelle des Problems:

Die Vorlage wird mehrfach instanziiert. Das liegt daran, dass Sie implizite Instanziierung verwenden. Einmal, wenn Sie die Klasse AA_API classA und einmal im Wesentlichen deklarieren, wenn Sie das Template < double> a deklarieren; in Haupt.

Dies bedeutet, dass Sie mehrere Definitionen für die Vorlage haben.

  • 1, bin ich nicht sicher, warum es im Release-Modus (es für den Mangel an mein tiefes Wissen mit Vorlagen)
  • 2,3,4 arbeiten, wenn Sie irgendeines der impliziten loszuwerden Template Instanziierung, werden Sie die multiple Definitionen
  • 5, vielleicht gcc tut die Instanziierung anders, oder es gibt eine Kraft multiplizieren Flagge irgendwo ... Ich weiß es nicht
  • 6, Dies löst nicht Ihr Problem, es zwingt nur dazu, die multiplen Augenblicke zu akzeptieren.

Lösung:

Explizite Instanziierung.

Hinweis: Wenn sich die Vorlagenklasse in einem anderen Projekt befindet, müssen Sie die Instanziierung exportieren.

Ich hoffe, dass dies Ihr Problem lösen wird. Es hat meine repariert.

+0

Vielen Dank für das Teilen. Aber die explizite Instanziierung in der Header-Datei löst mein Problem nicht. Wenn "template.h" in mehreren cpp enthalten ist, gibt es mehrere Definitionen. – flm8620

+0

Wenn ich diese explizite Instanziierung in Header-Datei hinzufüge, löst Schritt 4 nicht mehr mein Problem. – flm8620

+0

Ja, es sieht so aus, als ob die Art Ihres Problems etwas anders ist. Leider kenne ich die genaue Erklärung nicht, aber wenn ich den Konstruktor in Ihrer Template-Klasse durch den Standardkonstruktor ersetze, den er erstellt. Sie brauchen nicht einmal eine explizite Instanziierung. –

Verwandte Themen