完美转发无法 link 具有显式模板实例化指令(EIDir;a.k.a。手动实例化)
Perfect forwarding fails to link with explicit template instantiation directives (EIDir; a.k.a. manual instantiation)
问题是当构造函数定义隐藏在实现中时,为什么链接器无法完美转发模板 class 的构造函数(g++,版本 7.5,Ubuntu 18.4)文件 (*.cpp) 但在 main.cpp?
中显式实例化
具体来说:
fwd_machine.hpp:
#include <memory>
#include <stdexcept>
template<typename T>
struct some_type
{
some_type(int, void*); // ld error: undefined reference to `some_type<int>::some_type(int, void*)'
// if no EIDir, i.e., defined in the header,
// then it works fine:
//
//some_type(int, void*){}
//
// or:
// if explicit specialization used (with pass-through version in the header)
// then it also works fine:
//
// some_type(int, void*){
// throw std::runtime_error("Error: only specializations whould be called...");
// }
// okay
};
struct factory_t
{
template<typename some_t, typename... Args>
decltype(auto)
operator()(Args&&... args)
{
return std::make_unique<some_t>(some_t(std::forward<Args>(args)...));
}
};
template<typename FType, typename...Fargs>
constexpr
decltype(auto) launch(FType f, Fargs&&... args)
{
return f.template operator()<some_type<int>>(std::forward<Fargs>(args)...);
}
其中 launch()
函数模板运行一个通用仿函数,通过 r-value 获取可变参数,以便它们可以被转发。在这种情况下,some_type
的构造函数(通过从中生成 unique_ptr
)。
但是,我需要隐藏在实现文件中的构造函数定义:
fwd_machine.cpp:
#include "fwd_machine.hpp"
// if no EIDir, i.e., defined in the header,
// then it works fine...
// or:
// if explicit specialization used (with pass-through version in the header)
// then it also works fine:
//
//template<>
//some_type<int>::some_type(int, void*){} // okay
template<typename T>
some_type<T>::some_type(int, void*){} // ld error: undefined reference to `some_type<int>::some_type(int, void*)'
然后在主体中,我对整个 class 使用手动实例化:
main_fwd_issues.cpp:
#include <iostream>
#include "fwd_machine.hpp"
// g++ -std=c++14 fwd_machine.cpp main_fwd_issues.cpp -o mfi.exe
// if no EIDir or specialization, then it works fine...
//
template class some_type<int>;
int main(void)
{
int i{1};
void* p{nullptr};
auto uniq_p = launch(factory_t{}, i, p);
std::cout<<"Finishing up...\n";
return 0;
}
但是,如上所述,我收到链接器错误:undefined reference to `some_type<int>::some_type(int, void*)'
我找到的唯一解决方案是使用自动模板实例化(即在 header 中提供构造函数的定义);或者,使用显式特化(在 header 中定义一个不被调用的通用构造函数,并在实现文件中针对 <int>
进行特化)。我尝试的任何其他方法(限定构造函数的签名;未在 launch()
或 factory_t::operator()
中使用完美转发)都失败了。
我想了解为什么链接器无法使用 some_type
的手动实例化,以及是否仍然可以使用手动实例化(并避免特化)。
The complete definition must appear before the explicit instantiation of a class template
当您尝试在 main.cpp
中实例化它时,您的程序并非如此。
显式实例化 some_type<int>
的正确方法是添加
// in fwd_machine.hpp
extern template class some_type<int>;
// in fwd_machine.cpp (after the definition is complete):
template class some_type<int>;
问题是当构造函数定义隐藏在实现中时,为什么链接器无法完美转发模板 class 的构造函数(g++,版本 7.5,Ubuntu 18.4)文件 (*.cpp) 但在 main.cpp?
中显式实例化具体来说: fwd_machine.hpp:
#include <memory>
#include <stdexcept>
template<typename T>
struct some_type
{
some_type(int, void*); // ld error: undefined reference to `some_type<int>::some_type(int, void*)'
// if no EIDir, i.e., defined in the header,
// then it works fine:
//
//some_type(int, void*){}
//
// or:
// if explicit specialization used (with pass-through version in the header)
// then it also works fine:
//
// some_type(int, void*){
// throw std::runtime_error("Error: only specializations whould be called...");
// }
// okay
};
struct factory_t
{
template<typename some_t, typename... Args>
decltype(auto)
operator()(Args&&... args)
{
return std::make_unique<some_t>(some_t(std::forward<Args>(args)...));
}
};
template<typename FType, typename...Fargs>
constexpr
decltype(auto) launch(FType f, Fargs&&... args)
{
return f.template operator()<some_type<int>>(std::forward<Fargs>(args)...);
}
其中 launch()
函数模板运行一个通用仿函数,通过 r-value 获取可变参数,以便它们可以被转发。在这种情况下,some_type
的构造函数(通过从中生成 unique_ptr
)。
但是,我需要隐藏在实现文件中的构造函数定义: fwd_machine.cpp:
#include "fwd_machine.hpp"
// if no EIDir, i.e., defined in the header,
// then it works fine...
// or:
// if explicit specialization used (with pass-through version in the header)
// then it also works fine:
//
//template<>
//some_type<int>::some_type(int, void*){} // okay
template<typename T>
some_type<T>::some_type(int, void*){} // ld error: undefined reference to `some_type<int>::some_type(int, void*)'
然后在主体中,我对整个 class 使用手动实例化: main_fwd_issues.cpp:
#include <iostream>
#include "fwd_machine.hpp"
// g++ -std=c++14 fwd_machine.cpp main_fwd_issues.cpp -o mfi.exe
// if no EIDir or specialization, then it works fine...
//
template class some_type<int>;
int main(void)
{
int i{1};
void* p{nullptr};
auto uniq_p = launch(factory_t{}, i, p);
std::cout<<"Finishing up...\n";
return 0;
}
但是,如上所述,我收到链接器错误:undefined reference to `some_type<int>::some_type(int, void*)'
我找到的唯一解决方案是使用自动模板实例化(即在 header 中提供构造函数的定义);或者,使用显式特化(在 header 中定义一个不被调用的通用构造函数,并在实现文件中针对 <int>
进行特化)。我尝试的任何其他方法(限定构造函数的签名;未在 launch()
或 factory_t::operator()
中使用完美转发)都失败了。
我想了解为什么链接器无法使用 some_type
的手动实例化,以及是否仍然可以使用手动实例化(并避免特化)。
The complete definition must appear before the explicit instantiation of a class template
当您尝试在 main.cpp
中实例化它时,您的程序并非如此。
显式实例化 some_type<int>
的正确方法是添加
// in fwd_machine.hpp
extern template class some_type<int>;
// in fwd_machine.cpp (after the definition is complete):
template class some_type<int>;