是否可以导出一些类模板实例,同时让库的用户能够生成给定类模板的其他特化(编译可执行文件时)。
鉴于我有一个公共标头
// public.h
#pragma once
#ifndef DLL_BUILD
#define API __declspec(dllimport)
#else
#define API __declspec(dllexport)
#endif // !DLL_BUILD
#include <type_traits>
// dummy to generate .lib
struct API dummy
{
void be_dummy();
};
template <class T>
struct Foo
{
static T Sum(T a, T b)
{
static_assert(std::is_fundamental_v<T>);
return a + b;
}
};
通过这种声明类模板的方式,Foo
每个实例化都将发生在用户的可执行文件中。
但是,如果我定义Foo
为dllexport/dllimport
使用API
宏,则其每个Foo
特化都没有在 dll 中显式实例化,将无法链接。
// impl.cpp - dll
#include "public.h"
void dummy::be_dummy()
{
volatile int a = 0;
return;
}
template API struct Foo<int>;
///////////////////////////////////////////
// main.cpp - executable
#include "public.h"
#include <iostream>
int main()
{
dummy().be_dummy();
// std::cout << Foo<double>().Sum(4.12, 3.18) << std::endl; // Unresolved external symbol
std::cout << Foo<int>().Sum(60, 9) << std::endl; // executed within the dll
return 0;
}
那么,是否可以强制编译器在导出一个类模板实例时链接到现有的类模板实例,并生成另一个没有的类模板实例。
更新
我找到了解决方案,请参阅下面的答案。我留下旧的更新,以防有人会发现 SFINAE 的这种用法有帮助。
更新旧
我发现了一个涉及 SFINAE 的繁琐解决方案,但它导致定义一个类模板两次,因此非常容易出错。我不知道它是否可以用宏包裹起来,这样就可以只写一次。
// public.h
#pragma once
#ifndef DLL_BUILD
#define API __declspec(dllimport)
#else
#define API __declspec(dllexport)
#endif // !DLL_BUILD
#include <type_traits>
namespace templ_export
{
template <class T>
struct is_exported : std::false_type {};
// this can be placed to a separated header (i.e. Exported.hpp)
template <> struct is_exported<int> : std::true_type {};
template <class T>
struct API FooExported
{
static T Sum(T a, T b)
{
//static_assert(std::is_fundamental_v<T>);
return a + b;
}
};
template <class T>
struct FooNotExported
{
static T Sum(T a, T b)
{
//static_assert(std::is_fundamental_v<T>);
return a + b;
}
};
template <class T, bool = templ_export::is_exported<T>()>
struct GetFooExported
{
using type = FooNotExported<T>;
};
template <class T>
struct GetFooExported<T, true>
{
using type = FooExported<T>;
};
}
template <class T>
using Foo = typename templ_export::GetFooExported<T>::type;
/////////////////////////////////
// impl.cpp
#include "public.h"
void dummy::be_dummy()
{
volatile int a = 0;
return;
}
template struct API templ_export::FooExported<int>;