I have an SFINAE problem:
In the following code, I want the C++ compiler to pick the specialized functor and print "special", but it's printing "general" instead.
#include <iostream>
#include <vector>
template<class T, class V = void>
struct Functor {
void operator()() const {
std::cerr << "general" << std::endl;
}
};
template<class T>
struct Functor<T, typename T::Vec> {
void operator()() const {
std::cerr << "special" << std::endl;
}
};
struct Foo {
typedef std::vector<int> Vec;
};
int main() {
Functor<Foo> ac;
ac();
}
How can I fix it so that the specialized struct is used automatically? Note I don't want to directly specialize the Functor struct on Foo, but I want to specialize it on all types that have a Vec type.
P.S.: I am using g++ 4.4.4
Sorry for misleading you in the last answer, I thought for a moment that it would be simpler. So I will try to provide a complete solution here. The general approach to solve this type of problems is to write a traits helper template and use it together with enable_if (either C++11, boost or manual implementation) to decide a class specialization:
Trait
A simple approach, not necessarily the best, but simple to write would be:
template <typename T>
struct has_nested_Vec {
typedef char yes;
typedef char (&no)[2];
template <typename U>
static yes test( typename U::Vec* p );
template <typename U>
static no test( ... );
static const bool value = sizeof( test<T>(0) ) == sizeof(yes);
};
The approach is simple, provide two template functions, that return types of different sizes. One of which takes the nested Vec type and the other takes ellipsis. For all those types that have a nested Vec the first overload is a better match (ellipsis is the worst match for any type). For those types that don't have a nested Vec SFINAE will discard that overload and the only option left will be the ellipsis. So now we have a trait to ask whether any type has a nested Vec type.
Enable if
You can use this from any library, or you can roll your own, it is quite simple:
template <bool state, typename T = void>
struct enable_if {};
template <typename T>
struct enable_if<true,T> {
typedef T type;
};
When the first argument is false, the base template is the only option, and that does not have a nested type, if the condition is true, then enable_if has a nested type that we can use with SFINAE.
Implementation
Now we need to provide the template and the specialization that will use SFINAE for only those types with a nested Vec:
template<class T, class V = void>
struct Functor {
void operator()() const {
std::cerr << "general" << std::endl;
}
};
template<class T>
struct Functor<T, typename enable_if<has_nested_Vec<T>::value>::type > {
void operator()() const {
std::cerr << "special" << std::endl;
}
};
Whenever we instantiate Functor with a type, the compiler will try to use the specialization, which will in turn instantiate has_nested_Vec and obtain a truth value, passed to enable_if. For those types for which the value is false, enable_if does not have a nested type type, so the specialization will be discarded in SFINAE and the base template will be used.
Your particular case
In your particular case, where it seems that you don't really need to specialize the whole type but just the operator, you can mix the three elements into a single one: a Functor that dispatches to one of two internal templated functions based on the presence of Vec, removing the need for enable_if and the traits class:
template <typename T>
class Functor {
template <typename U>
void op_impl( typename U::Vec* p ) const {
std::cout << "specialized";
}
template <typename U>
void op_impl( ... ) const {
std::cout << "general";
}
public:
void operator()() const {
op_impl<T>(0);
}
};
Even though this is an old question, I think it's still worth providing a couple more alternatives for quickly fixing the original code.
Basically, the problem is not with the use of SFINAE (that part is fine, actually), but with the matching of the default parameter in the primary template (void) to the argument supplied in the partial specialization(typename T::Vec). Because of the default parameter in the primary template, Functor<Foo> actually means Functor<Foo, void>. When the compiler tries to instantiate that using the specialization, it tries to match the two arguments with the ones in the specialization and fails, as void cannot be substituted for std::vector<int>. It then falls back to instantiating using the primary template.
So, the quickest fix, which assumes all your Vecs are std::vector<int>s, is to replace the line
template<class T, class V = void>
with this
template<class T, class E = std::vector<int>>
The specialization will now be used, because the arguments will match. Simple, but too limiting. Clearly, we need to better control the type of the argument in the specialization, in order to make it match something that we can specify as the default parameter in the primary template. One quick solution that doesn't require defining new traits is this:
#include <iostream>
#include <vector>
#include <type_traits>
template<class T, class E = std::true_type>
struct Functor {
void operator()() const {
std::cerr << "general" << std::endl;
}
};
template<class T>
struct Functor<T, typename std::is_reference<typename T::Vec&>::type> {
void operator()() const {
std::cerr << "special" << std::endl;
}
};
struct Foo {
typedef std::vector<int> Vec;
};
int main() {
Functor<Foo> ac;
ac();
}
This will work for any Vec type that could make sense here, including fundamental types and arrays, for example, and references or pointers to them.
Another alternative for detecting the existence of a member type is to use void_t. As valid partial specialisations are preferable to the general implementation as long as they match the default parameter(s), we want a type that evaluates to void when valid, and is only valid when the specified member exists; this type is commonly (and, as of C++17, canonically) known as void_t.
template<class...>
using void_t = void;
If your compiler doesn't properly support it (in early C++14 compilers, unused parameters in alias templates weren't guaranteed to ensure SFINAE, breaking the above void_t), a workaround is available.
template<typename... Ts> struct make_void { typedef void type; };
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
As of C++17, void_t is available in the utilities library, in type_traits.
#include <iostream>
#include <vector>
#include <type_traits> // For void_t.
template<class T, class V = void>
struct Functor {
void operator()() const {
std::cerr << "general" << std::endl;
}
};
// Use void_t here.
template<class T>
struct Functor<T, std::void_t<typename T::Vec>> {
void operator()() const {
std::cerr << "special" << std::endl;
}
};
struct Foo {
typedef std::vector<int> Vec;
};
int main() {
Functor<Foo> ac;
ac();
}
With this, the output is special, as intended.
In this case, since we're checking for the existence of a member type, the process is very simple; it can be done without expression SFINAE or the type_traits library, allowing us to rewrite the check to use C++03 facilities if necessary.
// void_t:
// Place above Functor's definition.
template<typename T> struct void_t { typedef void type; };
// ...
template<class T>
struct Functor<T, typename void_t<typename T::Vec>::type> {
void operator()() const {
std::cerr << "special" << std::endl;
}
};
To my knowledge, this should work on most, if not all, SFINAE-capable C++03-, C++11-, C++14-, or C++1z-compliant compilers. This can be useful when dealing with compilers that lag behind the standard a bit, or when compiling for platforms that don't have C++11-compatible compilers yet.
For more information on void_t, see cppreference.
Related
I have this scenario:
#include <iostream>
class SomeClass
{
public:
int _int;
};
#define DO_SOME_STUFF(ptr) std::cout << /* Print the typeid().hash_code() of the type which ptr is poiting to (int) */;
int main()
{
int SomeClass::* ptr_to_int_member = &SomeClass::_int;
DO_SOME_STUFF(ptr_to_int_member)
}
I want to know which type is ptr pointing at (which is currently int).
Knowing which class owns that int is also useful (which is currently SomeClass).
You can do that with a "template trick":
template<typename T>
struct PointerToMemberDecomposer {};
template<typename T, typename P>
struct PointerToMemberDecomposer<P T::*>
{
using ClassType = T;
using MemberType = P;
};
And change your code to:
#include <iostream>
template<typename T>
struct PointerToMemberDecomposer {};
template<typename T, typename P>
struct PointerToMemberDecomposer<P T::*>
{
using ClassType = T;
using MemberType = P;
};
class SomeClass
{
public:
int _int;
};
#define DO_SOME_STUFF(ptr) std::cout << typeid(PointerToMemberDecomposer<decltype(ptr)>::MemberType).hash_code();
int main()
{
int SomeClass::* ptr_to_int_member = &SomeClass::_int;
DO_SOME_STUFF(ptr_to_int_member)
}
Defining a couple of templated aliases can make the code a little bit cleaner:
#define GET_POINTER_TO_MEMBER_CLASS_TYPE(ptr) PointerToMemberDecomposer<decltype(ptr)>::ClassType
#define GET_POINTER_TO_MEMBER_MEMBER_TYPE(ptr) PointerToMemberDecomposer<decltype(ptr)>::MemberType
So you can change DO_SOME_STUFF to:
#define DO_SOME_STUFF(ptr) std::cout << typeid(GET_POINTER_TO_MEMBER_MEMBER_TYPE(ptr)).hash_code();
Explanation
This technique is called Partial template specialization.
The second definition of PointerToMemberDecomposer will be used when a pointer-to-member type is passed as template argument; And will catch new T and P typenames. using those new typenames; It will define two type aliases (ClassType and MemberType) so T and P can be used outside of the PointerToMemberDecomposer struct.
When using PointerToMemberDecomposer; you should use decltype operator which acts like type in Python or typeof in C#. decltype(x) passes the type of x instead of x itself.
Update
As 463035818_is_not_a_number have mentioned; macros can be replaced with templated aliases
template <typename T>
using ClassTypeFromPtrToMember_t = typename PointerToMemberDecomposer<T>::ClassType;
template <typename T>
using MemberTypeFromPtrToMember_t = typename PointerToMemberDecomposer<T>::MemberType;
But you should still use decltype while DO_SOME_STUFF is a macro instead of a templated function and we cant access ptr's type directly (see 463035818_is_not_a_number's answer for templated function version of DO_SOME_STUFF):
#define DO_SOME_STUFF(ptr) std::cout << typeid(MemberTypeFromPtrToMember_t<decltype(ptr)>).hash_code();
In this case; DO_SOME_STUFF can be converted to a templated function. But you might want to for example fill a non capturing lambda with macro arguments; which requires DO_SOME_STUFF to be a macro.
Also, you might want to change ClassType and MemberType to type and create two separated structs (or classes) for retrieving those type aliases; If you want PointerToMemberDecomposer to look like C++'s standard library.
For more details; see 463035818_is_not_a_number's answer
Just summarizing comments to some otherwise great answer...
Member aliases are commonly named just type. Macros are better avoided (Why are preprocessor macros evil and what are the alternatives?) and for less verbosity on the caller you can use a function template:
#include <iostream>
#include <typeinfo>
template<typename T>
struct TypeFromPtrToMember; // needs no definition
template<typename T, typename P>
struct TypeFromPtrToMember<P T::*>
{
using type = T;
};
class SomeClass
{
public:
int _int;
};
template <typename T>
void do_some_stuff(T t){
std::cout << typeid(typename TypeFromPtrToMember<T>::type).hash_code();;
}
int main()
{
int SomeClass::* ptr_to_int_member = &SomeClass::_int;
do_some_stuff(ptr_to_int_member);
}
Naming the member alias type is so common that I would do it even if you then need two traits. The other trait is basically the same just with using type = P;.
In the above, there is still the little annoyance of having to write typename when using the trait (because TypeFromPtrToMember<T>::type is a dependent name). Since C++11 we can use a template alias to help with that. Template aliases cannot be partially specialized, but we already have the trait and just need to forward to that:
template <typename T>
using TypeFromPtrToMember_t = typename TypeFromPtrToMember<T>::type;
Such that do_some_stuff can be:
template <typename T>
void do_some_stuff(T t){
std::cout << typeid(TypeFromPtrToMember_t<T>).hash_code();;
}
I hope you agree that now no macros are needed anymore.
It is a bit strange. I am trying to write a code which detects whether a type is a string object. The char * or other object types should lead to false not true. The following code gives me:
error: template parameters not deducible in partial specialization:
I do not understand what the message means. Even searched online, I cannot figure out how to fix this:
#include <iostream>
#include <type_traits>
#include <string>
template <typename T>
struct is_string : std::false_type { };
template <typename T>
struct is_string<std::string> : std::true_type<T> { };
class Temp
{
int a;
};
int main()
{
// Expect: false
std::cout << is_string<int>::value << std::endl;
std::cout << is_string<char *>::value << std::endl;
std::cout << is_string<Temp>::value << std::endl;
// Expect: true
std::cout << is_string<std::string>::value << std::endl;
std::cout << is_string<const std::string>::value << std::endl;
std::cout << is_string<std::string&>::value << std::endl;
}
If there is a out of box std tool, that's welcome
The template specialization should look like this:
template <>
struct is_string<std::string> : std::true_type { };
But even if you use it, your template will return false for cv-qualified string or references to it.
I did not use std::is_same because it rejects types such as const string or string & as string.
The right way to do it is std::is_same_v<std::remove_cv_t<std::remove_reference_t<your_type>>, std::string> (or using std::decay_t, as the other answer suggests).
Or, in C++20: std::is_same_v<std::remove_cvref_t<<your_type>, std::string>.
"Typo" in specialization syntax, it should be:
template <>
struct is_string<string> : std::true_type {};
To handle const std::string or std::string&, you might compose with std::decay
template <typename T>
using is_string_like = is_string<std::decay_t<T>>;
or using std::is_same:
template <typename T>
using is_string_like = std::is_same<std::string, std::decay_t<T>>;
When you create a specialization, you can kinda think of the template parameters of the specialization as "variables" for the specialization. You can give as many as you want, but they must be used in the angle brackets that are part of the class.
When you create a full specialization, you do not provide any template parameters - empty angle brackets.
The other answers by HolyBlackCat and Jarod42 are correct, in that they get rid of your compiler error, and provide a working implementation for your vague specifications.
However, both of them suggest doing more than removing cv-qualifiers from the type. I do not recommend doing this, as it goes against how the type traits in the standard library work and it could be quite surprising to users.
Since you actually want that as a result, I would suggest providing a separate metafunction that uses the basic one, and make it clear that it is removing the reference.
static_assert(std::is_null_pointer<std::nullptr_t>::value);
static_assert(std::is_null_pointer<std::nullptr_t const volatile>::value);
static_assert(not std::is_null_pointer<std::nullptr_t &>::value);
static_assert(std::is_unsigned<unsigned long>::value);
static_assert(std::is_unsigned<unsigned long const volatile>::value);
static_assert(not std::is_unsigned<unsigned long &>::value);
Plus, std::string is just one of the possible standard string types. What about a string using a different character type (e.g., std::wstring), or the same character type but a different allocator (e.g., std::pmr::string)?
Those already show how to limit your trait for just std::string. Here is one way to do it for any standard string type.
namespace detail {
template <typename T>
struct is_string
: std::false_type
{};
// Partial specialization - parameters used to qualify the specialization
template <typename CharT, typename TraitsT, typename AllocT>
struct is_string<std::basic_string<CharT, TraitsT, AllocT>>
: std::true_type
{};
}
template <typename T>
using is_basic_string = detail::is_string<std::remove_cv_t<T>>;
static_assert(is_basic_string<std::string>::value);
static_assert(is_basic_string<std::string const volatile>::value);
static_assert(not is_basic_string<std::string &>::value);
static_assert(is_basic_string<std::wstring>::value);
static_assert(is_basic_string<std::pmr::string>::value);
The specialization should not include typename T, so:
template <>
struct is_string<std::string> : std::true_type<T> { };
Since you want const std::string and std::string& and combinations thereof to count as string too, you may find it easier to inherit from integral_constant instead (which is what false_type and true_type does).
Example:
#include <iostream>
#include <string>
#include <type_traits>
template <typename T>
struct is_string : std::integral_constant<bool,
std::is_same_v<std::string, std::decay_t<T>>> {};
// helper
template<typename T>
inline constexpr bool is_string_v = is_string<T>::value;
I have observed that whenever a template class is specialized (partially/completely), all the member functions needs to be explicitly defined otherwise there is an error. Here is an example below
#include <iostream>
template<typename T, typename U> //Primary
struct test
{
void f() { std::cout << "\nPrimary"; }
void g() { std::cout << "Called g()\n";}
};
template <typename T> //Specialization
struct test<T, int*>
{
void f() { std::cout << "\nPartial Specialization"; }
};
template<> //Full specialization
struct test<int*, int*>
{
void f() { std::cout << "\nFull Specialization\n"; }
};
int main()
{
test<int, double> t1;
t1.f();
t1.g();
test<double, int*> t2;
t2.f();
t2.g();
test<int*, int*> t3;
t3.f();
t3.g();
}
Here t2.g() and t3.g() gives compile time error as they are not explicitly defined. If for every specialization, the member functions needs to be defined again. What's the advantage of allow partial/fully specialization?
I think you've got the concept of class specializations wrong here.
Class specialization is not inheritance. The specialized class is a different class than the initial one. Nothing is shared between the two. And hence, there doesn't exist any g() method in the specialized ones.
If you're looking for a way to just have the methods different, you should look into method specializations instead.
I think the main purpose for specialization is defining "exceptions" if you want to handle some data types in different ways.
Looking at partial specialization consider the following:
// NOT specialized
template <typename T>
struct test <T, T>
{
...
};
// partially specialized
template <typename T>
struct test <T*, T*>
{
...
};
The latter example is already partially specialized because you are telling the compiler to expect any type of pointer. And this can be useful of course because you might want to handle pointer-types slightly different to non-pointer types (checking for being NULL for example)
I recommend reading this article
Here's my situaition. I'm trying to detect if a type has special methods utilized by nlohmann::json, namley to_json. Now I've seen the following solutions for free function checking with SFINAE:
Checking whether a function (not a method) exists in c++11 via templates
SFINAE: detect if class has free function
but these methods at least appear to rely on the return type of a function being void or not. in the case of to_json the signature is as follows:
void to_json(json& j, const T& p);
which returns void... thus makes these methods fail (the second doesn't really work regardless since defining a custom wrapper for each type simply is not feasible).
I modified the first method, and as expected:
#include <iostream>
#include <type_traits>
#include "json.hpp"
template<class...> struct voider { using type = void; };
template<class... T> using void_t = typename voider<T...>::type;
template<class T, class = void>
struct is_jstreamable : std::false_type{};
template<class T>
struct is_jstreamable<T, void_t<decltype(to_json(std::declval<nlohmann::json &>(),
std::declval<T>()))>> : std::true_type {};
struct Foo;
template<typename T>
typename std::enable_if<is_jstreamable<T>::value,
void>::type
bar(){
std::cout << "It works!" << std::endl;
};
template<typename T>
typename std::enable_if<!is_jstreamable<T>::value,
void>::type
bar(){
std::cout << "It doesn't work!" << std::endl;
}
int main(){
//int does have conversion
bar<int>();
//foo does not have conversion
bar<Foo>();
}
it fails to work because its void type, the console returning:
It doesn't work!
It doesn't work!
instead of the expected
It works!
It doesn't work!
I saw a method for determining if the return of a function is void but I'm unsure of how to incorprate that as a solution of my problem
nlohmann::json has multiple ways to convert a given type to json. There is no to_json defined for int, so your type trait is working as specified.
Instead, detect if you can convert the type to a nlohmann::json object:
template <typename T>
using is_jstreamable = std::is_convertible<T, nlohmann::json>;
Live on Godbolt
I have a templated function defined as:
template<typename TObject> TObject Deserialize(long version, const Value &value)
what I need to do, is to write a specialization which would take vector defined as:
template<typename TNum, int cnt> class Vec
and still has access to cnt and TNum.
I have unsuccesfully tried
template<typename TNum, int cnt> Vec<TNum, cnt> Deserialize<Vec<TNum, cnt>>(long version, Value &value)
resulting in error: illegal use of explicit template arguments
What is the correct way to do it?
Usually, the correct answer to dealing with function templates and needing to partially specialize them, is to simply overload them instead. In this case this trick doesn't work directly because there are no arguments that depend on the template parameter, i.e. the template parameter is explicitly specified and not deduced. However, you can forward along to implementation functions, and make overloading work by using a simple tag struct.
#include <functional>
#include <iostream>
#include <type_traits>
#include <vector>
#include <array>
template <class T>
struct tag{};
template<typename TObject>
TObject Deserialize_impl(long version, tag<TObject>) {
std::cerr << "generic\n";
return {};
}
template<typename T, std::size_t N>
std::array<T,N> Deserialize_impl(long version, tag<std::array<T,N>>) {
std::cerr << "special\n";
return {};
}
template<typename TObject>
TObject Deserialize(long version) {
return Deserialize_impl(version, tag<TObject>{});
}
int main() {
Deserialize<int>(0);
Deserialize<std::array<int,3>>(0);
return 0;
}
Live example: http://coliru.stacked-crooked.com/a/9c4fa84d2686997a
I generally find these approaches strongly preferable to partial specialization of a struct with a static method (the other major approach here) as there are many things you can take advantage with functions, and it behaves more intuitively compared to specialization. YMMV.
While the functional tag-dispatch is a nice approach, here's a class specialization version for comparison. Both have their use, and I don't think either is an inherently regrettable decision but maybe one matches your personal style more.
For any class you write that needs a custom deserialize handler, just write a specialization of the Deserializer class:
#include <iostream>
#include <string>
using namespace std;
using Value = std::string;
// default deserialize function
template <typename TObject>
struct Deserializer {
static TObject deserialize(long version, const Value &value) {
std::cout << "default impl\n";
return TObject();
}
};
// free standing function (if you want it) to forward into the classes
template <typename TObject>
TObject deserialize(long version, const Value &value) {
return Deserializer<TObject>::deserialize(version, value);
}
// Stub example for your Vec class
template<typename TNum, int cnt> class Vec { };
// Stub example for your Vec deserializer specialization
template <typename TNum, int cnt> struct Deserializer<Vec<TNum, cnt>> {
static auto deserialize(long version, const Value &value) {
std::cout << "specialization impl: cnt=" << cnt << "\n";
return Vec<TNum, cnt>();
}
};
int main() {
Value value{"abcdefg"};
long version = 1;
deserialize<int>(version, value);
deserialize<Vec<int, 10>>(version, value);
}
Ideally in this situation, Vec should reflect its own template parameters as members Vec::value_type and Vec::size() which should be constexpr.
If the class fails to provide its own properties in its own interface, the next best thing is to define your own extension interface. In this situation, you can have separate metafunctions (like accessor functions), or a traits class (like a helper view class). I'd prefer the latter:
template< typename >
struct vector_traits;
template< typename TNum, int cnt >
struct vector_traits< Vec< TNum, cnt > > {
typedef TNum value_type;
constexpr static int size = cnt;
};
template<typename TVec> TVec Deserialize(long version, Value &value) {
typedef vector_traits< TVec > traits;
typedef typename traits::value_type TNum;
constexpr static int cnt = traits::size;
…
}
This solution fits into any existing function, and even makes the signatures cleaner. Also, the function is more flexible because you can adapt it by adding traits specializations instead of entire new overloads.