很抱歉使用不重要的代码(也在wandbox here上)
#include <memory>
#include <cstdio>
template< typename T, size_t N, typename ... Args >
void test_1 ( Args ... args)
{
using namespace std;
if constexpr( 1 > (sizeof... (args)) ) {
return;
}
else
{
auto arg_list [[maybe_unused]] = { args ... };
}
}
template< typename T, size_t N, typename ... Args >
void test_2 ( Args ... args)
{
auto arg_list [[maybe_unused]] = { args ... };
}
///////////////////////////////////////////////////////////
int main()
{
test_1<int,3>(1,2,3);
test_2<int,3>(1,2,3);
// taking the function pointer (aka address of) leads to
// full instantiation of function template
constexpr auto adr_1 [[maybe_unused]] = std::addressof(test_1<int,3>);
// no can do --> constexpr auto adr_2 = std::addressof(test_2<int,3>);
}
所以。从上面看来,似乎采用了模板函数指针(又称地址)导致了函数模板的完整实例化。
这是一种不幸。如果使用模板函数实例化的地址,则将产生完整的实例化。即使一个都不被调用。
// pointer to instance made but instance never used
constexpr auto adr_5 [[maybe_unused]] = std::addressof(test_1<float,9>);
// pointer to instance made but instance never used
constexpr auto adr_of_big_foot_gun
[[maybe_unused]] = std::addressof(test_1<bool,99>);
// pointer to instance made but instance never used
constexpr auto adr_of_crazy [[maybe_unused]] =
std::addressof(test_1<char, 0xFFFF>);
现在。如何规避?请注意,上面的代码意味着编译器会执行此操作:
// this line
std::addressof( test_2<int,3> )
// provokes something like this
// which needs to be compiled
test_2<int,3>(void) ;
这就是为什么可以接受上面test1
而不是test_2
的地址的原因。也许对我来说,这根本没有任何意义。通过这个,我特别地意味着编译函数模板实例,就像通过使用void参数调用它一样?反过来,这意味着如果没有constexpr-if
,则很难在上面编写test_1。反过来(几乎)意味着没有c ++ 11和c ++ 14。
请讨论...
答案 0 :(得分:6)
这是一种不幸。如果采用模板地址 函数实例化,将产生完整的实例化。
这不是不幸的。这就是语言的工作方式。如果获得模板函数的地址(从函数模板实例化的函数),则此函数必须存在。换句话说,必须从相应的功能模板中实例化它。
如何规避?
没有办法。这是一种合理的预期行为。
答案 1 :(得分:0)
In order to take an address, you need a complete instantiation, otherwise there is nothing to point to. Remember that the compiler will not be there at runtime to finish generation of a partially parametrized template. Even at a different callsite that only knows about the function pointer, the compiler cannot instantiate the template, because it may not know about it.
The instances of test2<int, 3, ...>
(or test1
for that matter) are different functions, depending on the arguments, there can never be one address for all of them. For example, test2<int,3, int>
is different from test2<int, 3, int, int>
, it takes different arguments and does different things with them. Their addresses are different, as is the type of the corresponding function pointer.
Potentially, depending on how you use the addresses, you may be able to switch them off at compile time by template parametrisation (if the pointers themselves are part of a template that is or is not instantiated) or constexpr-if constructs.
Again potentially, if there remains no reference whatsoever to the pointers in all of the object files, the linker may remove the unnecessarily instantiated templates as well with the right options, but this is not guaranteed, and you will still pay the compile time cost.