I've read answers to this question, peeked at gcc implementation for std::any
, and didn't quite understand the implementation. However, I thought to try out the idea of using function pointers to deduce types.
In debug I get expected results, but in release my template specializations are optimized away... I've tried to use #pragma optimize("", off)
and __declspec(noinline)
but it didn't help.
AFAIK comparing pointers to functions is not UB. So I guess that MSVC (in my case) optimizes template specializations away...
Is there a way to make this work?
#include <iostream>
#include <memory>
template <typename T>
void deduce_type()
{}
class any
{
/*
template <typename T, typename ... Args>
any(Args&& ... args)
: ptr_(new T(std::forward<Args>(args) ...), std::default_delete<T>())
{}
*/
public:
template <typename T>
any(T&& other)
: ptr_(new T(std::forward<T>(other))),
deduce_type_(&deduce_type<T>)
{}
template <typename T>
bool contains_type() const
{
return deduce_type_ == &deduce_type<T>;
}
private:
// std::unique_ptr<void, std::default_delete<void>> ptr_;
void* ptr_;
void(*deduce_type_)();
};
struct Foo
{
};
int main()
{
any anyInt = 16;
any anyInt2 = 17;
any anyDouble = 2.0;
any anyFoo = Foo();
bool isInt = anyInt.contains_type<int>(),
isInt2 = anyInt2.contains_type<int>(),
notDouble = anyInt.contains_type<double>(), // 0 expected
isDouble = anyDouble.contains_type<double>(),
isFoo = anyFoo.contains_type<Foo>(),
notFoo = anyInt.contains_type<Foo>(); // 0 expected
std::cout << "is int = " << isInt << std::endl;
std::cout << "is int = " << isInt2 << std::endl;
std::cout << "is not double = " << notDouble << std::endl;
std::cout << "is double = " << isDouble << std::endl;
std::cout << "is Foo = " << isFoo << std::endl;
std::cout << "is not Foo = " << notFoo << std::endl;
return 0;
}
Release output:
is int = 1
is int = 1
is not double = 1
is double = 1
is Foo = 1
is not Foo = 1
foo is deducible = 1
Debug and expected output:
is int = 1
is int = 1
is not double = 0
is double = 1
is Foo = 1
is not Foo = 0
foo is deducible = 1
So according to the comments the problem is caused by the linker when Identical Code Folding
is enabled (by default in Release in MSVC). I've found no ways to disable this linker flags in code for separate functions, so there is nothing to do but to invent some ugly workarounds that would prevent functions from being folded to one address. That is by forcing them somehow to generate "unique" instructions for each template instantiation...
When I change the signature of a template function to simply return an integer, I get desired results, but only when particular specializations have been explicitly provided...
template <typename T>
size_t deduce_type()
{
return 0;
}
template <>
size_t deduce_type<double>()
{
return 1;
}
template <>
size_t deduce_type<Foo>()
{
return 2;
}
I get expected output in this case even if the same value is returned in different specializations. This, however, holds true only if specializations return different value then the basic template.
I guess I have to open another question...