Problem Statement
I'm trying to pass in a struct that contains a generic attribute like such
template <typename Value>
struct ColumnValue {
std::string columnName;
Value value;
};
I'd also like to create a function that accepts an unknown number of parameters as such
print(T... args)
These args will be of the type ColumnValue objects with 1 or more...
I'd like the print function to do different things depending on what type "Value" is.
Desired Result
222
"hellooooo"
Code
#include <iostream>
template <typename Value>
struct ColumnValue {
std::string columnName;
Value value;
};
template <template<typename> typename ...X, typename ...Y>
void print(std::string firstArg, const X<Y>& ...args) {
for(auto val : {args...}) {
std::cout << val.value << std::endl;
}
}
int main() {
ColumnValue<int> v{
.columnName="hello",
.value=222
};
ColumnValue<std::string> d{
.columnName="hello",
.value="hellooooo"
};
print("", v, d);
return 0;
}
Error Message
: In instantiation of ‘void print(std::string, const X&
...) [with X = {ColumnValue, ColumnValue}; Y = {int,
std::__cxx11::basic_string,
std::allocator >}; std::string =
std::__cxx11::basic_string]’: :28:19: required from
here :12:5: error: unable to deduce
‘std::initializer_list&&’ from ‘{args#0, args#1}’ 12 |
for(auto val : {args...}) {
| ^~~ :12:5: note: deduced conflicting types for parameter ‘auto’ (‘ColumnValue’ and
‘ColumnValue >’)
The fact that ColumnValue is a template doesn't make any difference for the signature of print. We can just take a regular parameter pack and let the compiler figure out the different types.
Secondly we can't loop over a parameter pack. We can however use a fold-expression.
The end result would look something like this
template <typename... T>
void print(std::string firstArg, const T& ...args) {
(std::cout << ... << args.value) << std::endl;
}
If you want to insert a newline between each argument, you would need some kind of helper for that. The simplest idea would be.
template <typename T>
void print_helper(const T& arg) {
std::cout << arg << '\n';
}
template <typename... T>
void print(std::string firstArg, const T& ...args) {
(print_helper(args.value), ...);
}
Related
Let's create currying function.
template <typename TFunc, typename TArg>
class CurryT
{
public:
CurryT(const TFunc &func, const TArg &arg)
: func(func), arg(arg )
{}
template <typename... TArgs>
decltype(auto) operator()(TArgs ...args) const
{ return func(arg, args...); }
private:
TFunc func;
TArg arg ;
};
template <typename TFunc, typename TArg>
CurryT<decay_t<TFunc>, remove_cv_t<TArg>>
Curry(const TFunc &func, const TArg &arg)
{ return {func, arg}; }
And function that decouple function to single argument functions:
// If single argument function (F(int)).
template <typename F>
static auto Decouple(const F &f, enable_if_t<is_invocable_v<F, int>> * = nullptr)
{
return f;
}
// If multiple arguments function (F(int, int, ...)).
template <typename F>
static auto Decouple(const F &f, enable_if_t<!is_invocable_v<F, int>> * = nullptr)
{
return [f](int v) { return Decouple( Curry(f, v) ); };
}
Everything works fine if 2 arguments function is passed:
auto f1 = Decouple(
[](int a, int b)
{ std::cout << a << " " << b << std::endl; }
);
f1(3)(4); // Outputs 3 4
But if I add more arguments
auto f2 = Decouple(
[](int a, int b, int c)
{ std::cout << a << " " << b << " " << c << std::endl; }
);
f(5)(6)(7);
The compilation breaks: https://coliru.stacked-crooked.com/a/10c6dba670d17ffa
main.cpp: In instantiation of 'decltype(auto) CurryT<TFunc, TArg>::operator()(TArgs ...) const [with TArgs = {int}; TFunc = main()::<lambda(int, int, int)>; TArg = int]':
main.cpp:17:26: error: no match for call to '(const main()::<lambda(int, int, int)>) (const int&, int&)'
17 | { return func(arg, args...); }
It breaks in instantiation of std::is_invocable.
Since debugging the standard library is hard, I created simple versions of standard type traits classes:
template <typename F> true_type check(const F &, decltype( declval<F>()(1) )* );
template <typename F> false_type check(const F &, ...);
template <typename F>
struct invocable_with_int : decltype(check(declval<F>(), nullptr))
{};
template <typename F>
inline constexpr bool invocable_with_int_v = invocable_with_int<F>::value;
template<bool B>
struct my_enable_if {};
template<>
struct my_enable_if<true>
{ using type = void; };
template <bool B>
using my_enable_if_t = typename my_enable_if<B>::type;
The problem remains the same https://coliru.stacked-crooked.com/a/722a2041600799b0:
main.cpp:29:73: required by substitution of 'template<class F> std::true_type check(const F&, decltype (declval<F>()(1))*) [with F = CurryT<main()::<lambda(int, int, int)>, int>]'
It tries to resolve calling to this function:
template <typename F> true_type check(const F &, decltype( declval<F>()(1) )* );
But decltype (declval<F>()(1))*) fails. But shouldn't this function be removed from overload resolution because template substitution fails? It works when Decouple is called first time. But when it is called second time the SFINAE seems to be disabled, and the first failure of template substitution gives a compilation error. Are there some limitation on secondary SFINAE? Why calling template function recursively doesn't work?
The problem is reproduced in GCC and Clang. So it is not a compiler bug.
Your operator() overload is completely unconstrained and therefore claims to be callable with any set of arguments. Only declarations, not definitions, are inspected to determine which function to call in overload resolution. If substitution into the definition then fails, SFINAE does not apply.
So, constrain your operator() to require TFunc to be callable with TArg and TArgs... as arguments.
For example:
template <typename... TArgs>
auto operator()(TArgs ...args) const -> decltype(func(arg, args...))
For me it is strange that your CurryT::operator() accepts unknown number of arguments.
Since aim is to have a functions which accept only one argument I expected that this function will accept only one argument.
IMO depending what kind of function CurryT holds CurryT::operator() should return a different type: return type of starting function or another version of CurryT.
Here is my approach using std::bind_front from C++20:
namespace detail {
template <typename TFunc>
class CurryT
{
public:
explicit CurryT(TFunc f) : mF(std::move(f))
{}
template<typename T>
auto get(T&& x, int = 0) -> decltype(std::declval<TFunc>()(x)) {
return mF(x);
}
template<typename T>
auto get(T&& x, char) {
return CurryT<decltype(std::bind_front(mF, std::forward<T>(x)))>{
std::bind_front(mF, std::forward<T>(x))
};
}
template<typename T>
auto operator()(T&& x)
{
return this->get(std::forward<T>(x), 1);
}
private:
TFunc mF;
};
}
template<typename F>
auto Decouple(F&& f)
{
return detail::CurryT<std::decay_t<F>>{std::forward<F>(f)};
}
https://godbolt.org/z/eW9r4Y6Ea
Note with this approach integer argument is not forced like in your solution.
Triggered by a comment to this answer I would like to write (in C++11) a
template <typename T>
struct has_out_op { static const bool value = ???; }
to dis/enable a member function depending on std::cout << t; being valid for some T t. I came this far...
#include <iostream>
struct can_convert_to_base{}; // but does not when there is a better match
struct base {base(can_convert_to_base);};
template <typename T>
auto test(const T& t,can_convert_to_base)
-> decltype( std::cout << t);
template <typename T>
std::false_type test(const T& t,base);
template <typename T>
struct has_out_op {
static const bool value =
!std::is_same<std::false_type,
decltype( test(T(),can_convert_to_base()) )
>::value;
};
struct A{};
int main() {
std::cout << has_out_op<int>::value; // prints 1
std::cout << has_out_op<A>::value; // prints 0
}
This seems to work, but when I use it for what I was actually aiming for:
struct B {
template <typename T>
typename std::enable_if<has_out_op<T>::value,B&>::type operator<<(const T& t) {
std::cout << t;
return *this;
}
};
int main() {
B b;
b << "1";
}
I get the error
prog.cc: In instantiation of 'const bool has_out_op<char [2]>::value':
prog.cc:25:60: required by substitution of 'template<class T> typename std::enable_if<has_out_op<T>::value, B&>::type B::operator<<(const T&) [with T = char [2]]'
prog.cc:31:14: required from here
prog.cc:17:67: error: functional cast to array type 'char [2]'
decltype( test(T(),can_convert_to_base()) )
^
prog.cc: In function 'int main()':
prog.cc:31:11: error: no match for 'operator<<' (operand types are 'B' and 'const char [2]')
b << "1";
^
Then I realized that my has_out_op requires T to be default constructible, and since that I am turning in circles. When I have a value I can easily test if std::cout << t; is valid, but with the type alone I have no idea how to properly implement has_out_op.
How to detect if there is a matching overload for std::cout << t; given only decltype(t)?
Note that I already know how to dis/enable B::operator<< properly, but out of courisity I am still struggling with getting has_out_op right.
std::declval<T>() to the rescue:
Converts any type T to a reference type, making it possible to use
member functions in decltype expressions without the need to go
through constructors.
Note that because no definition exists for declval, it can only be
used in unevaluated contexts; i
...
decltype( test(std::declval<T>(),can_convert_to_base()) )
...
Since we're already here, your solution is overly complicated. This is how I would do it:
struct B {
template <typename T, class = decltype(std::cout << std::declval<T>())>
B& operator<<(const T& t)
{
std::cout << t;
return *this;
}
};
though I would be interested if there is a simpler solution for
has_out_op
template <typename T>
struct has_out_op_impl
{
template <class U, class = decltype(std::cout << std::declval<U>())>
static auto foo(U) -> std::true_type;
static auto foo(...) -> std::false_type;
using Type = decltype(foo(std::declval<T>()));
};
template <class T>
struct has_out_op : has_out_op_impl<T>::Type
{};
struct A{};
int t1()
{
static_assert(has_out_op<int>::value == true, "");
static_assert(has_out_op<A>::value == false, "");
}
I want to write a function that accepts a variable number of string literals. If I was writing in C, I would have to write something like:
void foo(const char *first, ...);
and then the call would look like:
foo( "hello", "world", (const char*)NULL );
It feels like it ought to be possible to do better in C++. The best I have come up with is:
template <typename... Args>
void foo(const char* first, Args... args) {
foo(first);
foo(args);
}
void foo(const char* first) { /* Do actual work */ }
Called as:
foo("hello", "world");
But I fear that the recursive nature, and the fact that we don't do any type checking until we get to a single argument, is going to make errors confusing if somebody calls foo("bad", "argument", "next", 42). What I want to write, is something like:
void foo(const char* args...) {
for (const char* arg : args) {
// Real work
}
}
Any suggestions?
Edit: There is also the option of void fn(std::initializer_list<const char *> args), but that makes the call be foo({"hello", "world"}); which I want to avoid.
I think you probably want something like this:
template<class... Args,
std::enable_if_t<(std::is_same_v<const char*, Args> && ...), int> = 0>
void foo(Args... args ){
for (const char* arg : {args...}) {
std::cout << arg << "\n";
}
}
int main() {
foo("hello", "world");
}
Note: it is not possible to match just string literals. The closest you can come is to match a const char array.
To do the type checking, use a function template which takes const char arrays.
To loop over them with range-based for, we need to convert it to an initializer_list<const char*>. We can do so directly with braces in the range-based for statement, because arrays will decay to pointers.
Here is what the function template looks like (note: this works on zero or more string literals. If you want one or more, change the function signature to take at least one parameter.):
template<size_t N>
using cstring_literal_type = const char (&)[N];
template<size_t... Ns>
void foo(cstring_literal_type<Ns>... args)
{
for (const char* arg : {args...})
{
// Real work
}
}
While all other answers solve the problem, you could also do the following:
namespace detail
{
void foo(std::initializer_list<const char*> strings);
}
template<typename... Types>
void foo(const Types... strings)
{
detail::foo({strings...});
}
This approach seems (at least to me) to be more readable than using SFINAE and works with C++11. Moreover, it allows you to move implementation of foo to a cpp file, which might be useful too.
Edit: at least with GCC 8.1, my approach seems to produce better error message when called with non const char* arguments:
foo("a", "b", 42, "c");
This implementation compiles with:
test.cpp: In instantiation of ‘void foo_1(const ArgTypes ...) [with ArgTypes = {const char*, int, const char*, const char*}]’:
test.cpp:17:29: required from here
test.cpp:12:16: error: invalid conversion from ‘int’ to ‘const char*’ [-fpermissive]
detail::foo({strings...});
~~~~~~~~~~~^~~~~~~~~~~~~~
While SFINAE-based (liliscent's implementation) produces:
test2.cpp: In function ‘int main()’:
test2.cpp:14:29: error: no matching function for call to ‘foo(const char [6], const char [6], int)’
foo("hello", "world", 42);
^
test2.cpp:7:6: note: candidate: ‘template<class ... Args, typename std::enable_if<(is_same_v<const char*, Args> && ...), int>::type <anonymous> > void foo(Args ...)’
void foo(Args... args ){
^~~
test2.cpp:7:6: note: template argument deduction/substitution failed:
test2.cpp:6:73: error: no type named ‘type’ in ‘struct std::enable_if<false, int>’
std::enable_if_t<(std::is_same_v<const char*, Args> && ...), int> = 0>
+1 for the C++17 liliscent's solution.
For a C++11 solution, a possible way is create a type traits to make an "and" of multiple values (something similar to std::conjunction that, unfortunately, is available only starting from C++17... when you can use folding and you don't need std::conjunction anymore (thanks liliscent)).
template <bool ... Bs>
struct multAnd;
template <>
struct multAnd<> : public std::true_type
{ };
template <bool ... Bs>
struct multAnd<true, Bs...> : public multAnd<Bs...>
{ };
template <bool ... Bs>
struct multAnd<false, Bs...> : public std::false_type
{ };
so foo() can be written as
template <typename ... Args>
typename std::enable_if<
multAnd<std::is_same<char const *, Args>::value ...>::value>::type
foo (Args ... args )
{
for (const char* arg : {args...}) {
std::cout << arg << "\n";
}
}
Using C++14, multAnd() can be written as a constexpr function
template <bool ... Bs>
constexpr bool multAnd ()
{
using unused = bool[];
bool ret { true };
(void)unused { true, ret &= Bs ... };
return ret;
}
so foo() become
template <typename ... Args>
std::enable_if_t<multAnd<std::is_same<char const *, Args>::value ...>()>
foo (Args ... args )
{
for (const char* arg : {args...}) {
std::cout << arg << "\n";
}
}
--- EDIT ---
Jarod42 (thanks!) suggest a far better way to develop a multAnd; something as
template <typename T, T ...>
struct int_sequence
{ };
template <bool ... Bs>
struct all_of : public std::is_same<int_sequence<bool, true, Bs...>,
int_sequence<bool, Bs..., true>>
{ };
Starting from C++14 can be used std::integer_sequence instead of it's imitation (int_sequence).
Using C++17 fold expressions on the comma operator, you can simply do the following:
#include <iostream>
#include <string>
#include <utility>
template<typename OneType>
void foo_(OneType&& one)
{
std::cout << one;
}
template<typename... ArgTypes>
void foo(ArgTypes&&... arguments)
{
(foo_(std::forward<ArgTypes>(arguments)), ...);
}
int main()
{
foo(42, 43., "Hello", std::string("Bla"));
}
Live demo here. Note I used foo_ inside the template, because I couldn't be bothered to write out 4 overloads.
If you really really really want to restrict this to string literals, change the function signature as Nevin's answer suggests:
#include <cstddef>
#include <iostream>
#include <string>
#include <utility>
template<std::size_t N>
using string_literal = const char(&)[N];
template<std::size_t N>
void foo(string_literal<N> literal)
{
std::cout << literal;
}
template<std::size_t... Ns>
void foo(string_literal<Ns>... arguments)
{
(foo(arguments), ...);
}
int main()
{
foo("Hello", "Bla", "haha");
}
Live demo here.
Note this is extremely close to the C++11 syntax to achieve the exact same thing. See e.g. this question of mine.
Well, the nearest you can get to a function accepting any arbitrary number of const char* but nothing else uses a template-function and forwarding:
void foo_impl(std::initializer_list<const char*> args)
{
...
}
template <class... ARGS>
auto foo(ARGS&&... args)
-> foo_impl({std::forward<ARGS>(args)...})
{
foo_impl({std::forward<ARGS>(args)...});
}
The subtlety is in allowing the normal implicit conversions.
#include<type_traits>
#include<iostream>
auto function = [](auto... cstrings) {
static_assert((std::is_same_v<decltype(cstrings), const char*> && ...));
for (const char* string: {cstrings...}) {
std::cout << string << std::endl;
}
};
int main(){
const char b[]= "b2";
const char* d = "d4";
function("a1", b, "c3", d);
//function(a, "b", "c",42); // ERROR
}
And now... for something completely different...
You can write a type wrapper struct as follows
template <typename, typename T>
struct wrp
{ using type = T; };
template <typename U, typename T>
using wrp_t = typename wrp<U, T>::type;
and a foo() function receiving a variadic list of char const * simply become
template <typename ... Args>
void foo (wrp_t<Args, char const *> ... args)
{
for ( char const * arg : {args...} )
std::cout << "- " << arg << std::endl;
}
The problem is that you can't call it as you want
foo("hello", "world");
because the compiler isn't able to deduce the Args... types.
Obviously you can explicit a list of dummy types
foo<void, void>("hello", "world");
but I understand that is a horrible solution.
Anyway, if you accept to pass through a trivial template function
template <typename ... Args>
void bar (Args ... args)
{ foo<Args...>(args...); }
you can call
bar("hello", "world");
The following is a full C++11 working example
#include <iostream>
template <typename, typename T>
struct wrp
{ using type = T; };
template <typename U, typename T>
using wrp_t = typename wrp<U, T>::type;
template <typename ... Args>
void foo (wrp_t<Args, char const *> ... args)
{
for ( char const * arg : {args...} )
std::cout << "- " << arg << std::endl;
}
template <typename ... Args>
void bar (Args ... args)
{ foo<Args...>(args...); }
int main ()
{
bar("hello", "world"); // compile
// bar("hello", "world", 0); // compilation error
}
Of course it is possible, this compiles and runs what you want (pay attention)
#include<iostream>
template<class... Char>
// hehe, here is the secret
auto foo(const Char*... args ) ->decltype((char const*)(*std::begin({args...})), (char const*)(*std::end({args...})), void(0))
{
for (const char* arg : {args...}) {
std::cout << arg << "\n";
}
}
int main() {
foo("no", "sense","of","humor");
}
This is #liliscent solution but with more sugar and, to please #rubenvb, without enable_if.
If you think the extra code as a comment (which is not), note that you'll see exactly the syntax you are looking for.
Note that you can only feed an homogeneous list of things that is convertible to char const*, which was one of your goals it seems.
Trying to specialize member methods.
Reading this previous question: std::enable_if to conditionally compile a member function
I can quite understand what I am doing wrong.
#include <string>
#include <iostream>
#include <type_traits>
template<typename T>
class Traits
{
};
struct Printer
{
template<typename T>
typename std::enable_if<!std::is_function<decltype(Traits<T>::converter)>::value, void>::type
operator()(T const& object)
{
std::cout << object;
}
template<typename T>
typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, void>::type
operator()(T const& object)
{
std::cout << Traits<T>::converter(object);
}
};
template<>
class Traits<std::string>
{
public:
static std::size_t converter(std::string const& object)
{
return object.size();
}
};
int main()
{
using namespace std::string_literals;
Printer p;
p(5);
p("This is a C-string");
p("This is a C++String"s); // This compiles.
}
Compilation Gives:
> g++ -std=c++1z X.cpp
X.cpp:42:5: error: no matching function for call to object of type 'Printer'
p(5);
^
X.cpp:14:5: note: candidate template ignored: substitution failure [with T = int]: no member named 'converter' in 'Traits<int>'
operator()(T const& object)
^
X.cpp:20:5: note: candidate template ignored: substitution failure [with T = int]: no member named 'converter' in 'Traits<int>'
operator()(T const& object)
^
They both seem to fail because they can't see the method converter. But I am trying to use SFINE and std::enable_if to recognize that this function does not exist and thus only instantiate one of the methods.
The same error is generated for each of the types:
X.cpp:43:5: error: no matching function for call to object of type 'Printer'
p("This is a C-string");
^
X.cpp:14:5: note: candidate template ignored: substitution failure [with T = char [19]]: no member named 'converter' in 'Traits<char [19]>'
operator()(T const& object)
^
X.cpp:20:5: note: candidate template ignored: substitution failure [with T = char [19]]: no member named 'converter' in 'Traits<char [19]>'
operator()(T const& object)
^
Note: It compiles for the std::string version.
You could defer to a private helper function, and use overload resolution to prefer to the positively SFINAE-d overload - and not have a negatively SFINAE-d one:
struct Printer
{
template <class T>
void operator()(T const& object) {
call_impl(object, 0);
}
private:
// selected if Traits<T>::converter exists and is a function
// preferred in this case because int is better than ...
template<typename T>
typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, void>::type
call_impl(T const& object, int)
{
std::cout << Traits<T>::converter(object);
}
// selected if either Traits<T>::converter doesn't exist or isn't a function
template<typename T>
void call_impl(T const& object, ...)
{
std::cout << object;
}
};
One of the nice benefits that we'll get in C++2a with constraining functions is that we can do this without the extra helper:
struct Printer
{
template <class T>
requires std::is_function<decltype(Traits<T>::converter)>::value
void operator()(T const& object)
{
std::cout << Traits<T>::converter(object);
}
template <class T>
void operator()(T const& object)
{
std::cout << object;
}
};
The problem is in how SFINAE works. When substitution fails, the entire function is taken out of the program. So even though your predicate typename std::enable_if<!std::is_function<decltype(Traits<T>::converter)>::value, void>::type is meant to catch the false case, the non-existence of converter will cause the overload to be taken off the table.
The easiest workaround is something like this:
struct Printer
{
template<typename T>
void
impl(T const& object, ...)
{
std::cout << object;
}
template<typename T>
typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, void>::type
impl(T const& object, void*)
{
std::cout << Traits<T>::converter(object);
}
template<typename T>
void
operator()(T const& x)
{
return impl(x, nullptr);
}
};
Basically: You give the compiler something that will always work without using the predicate. The trick here is that nullptr will be matched to void* instead of ..., so it will do what you want.
If you want to get real fun about it, you can make a has_converter function whose return type is true_type or false_type and overload the implementation on that.
struct Printer
{
template<typename T>
std::false_type
has_converter(T const& object, ...);
template<typename T>
typename std::enable_if<std::is_function<decltype(Traits<T>::converter)>::value, std::true_type>::type
has_converter(T const& object, void*);
template<typename T>
void impl(T const& x, std::false_type)
{
std::cout << x;
}
template<typename T>
void impl(T const& x, std::true_type)
{
std::cout << Traits<T>::converter(x);
}
template<typename T>
void
operator()(T const& x)
{
return impl(x, decltype(has_converter(x, nullptr))());
}
};
One can imagine a helper function or templated constexpr bool to make using this property even easier (use the same technique as above).
template <typename T>
constexpr bool has_converter = ???;
How about adding non-function converter to non-specialized trait?
template<typename T>
class Traits
{
public: enum class Dummy{nothing};
public: static Dummy const converter = Dummy::nothing;
};
Run this code online
In the example below, I need to extract some values. I have an efficient extractor, which can work with builtin types, and an inefficient template that can work with everything. To choose between these, I want to use Function Overloading Based on Arbitrary Properties of Types. Here is my code:
#include <string>
#include <iostream>
class extractor
{
public:
static void extract(const bool& val) { std::cout << "Specialized extractor called" << std::endl; }
static void extract(const double& val) { std::cout << "Specialized extractor called" << std::endl; }
};
template <typename T>
void extract_generic(const T& value) { std::cout << "Generic extractor called" << std::endl; }
template <typename T> struct is_extractor_native { static const bool val = false; };
template<> struct is_extractor_native<bool> {static const bool val = true; };
template<> struct is_extractor_native<double> {static const bool val = true; };
template <bool B, class T = void>
struct enable_if {
typedef T type;
};
template <class T>
struct enable_if<false, T> {};
template <typename T>
struct extract_caller
{
//line 32 below
static void extract(const T& val, typename enable_if<is_extractor_native<T>::val>::type * = 0)
{
extractor::extract(val);
}
//line 37 below
static void extract(const T& val, typename enable_if<!is_extractor_native<T>::val>::type * = 0)
{
extract_generic(val);
}
};
int main(void)
{
std::string string_value("hello");
double double_value(.123);
std::cout << "native extractor for std::string: " << (int)is_extractor_native<std::string>::val << std::endl;
std::cout << "native extractor for double: " << (int)is_extractor_native<double>::val << std::endl;
extract_caller<std::string>::extract(string_value);
extract_caller<double>::extract(double_value);
return 0;
}
When I build the compiler complains:
g++ main.cpp -o main
main.cpp: In instantiation of ‘extract_caller<std::basic_string<char> >’:
main.cpp:50:29: instantiated from here
main.cpp:32:14: error: no type named ‘type’ in ‘struct enable_if<false, void>’
main.cpp: In instantiation of ‘extract_caller<double>’:
main.cpp:51:24: instantiated from here
main.cpp:37:14: error: no type named ‘type’ in ‘struct enable_if<false, void>’
make: *** [main] Error 1
When comment out the extraction and have only the traits printed, I get correct results:
./main
native extractor for std::string: 0
native extractor for double: 1
On the error listing you can see that for double the compiler passes the prototype on line 32, goes to 37 and prints error. The question is, why the SFINAE principle is not applied here?
SFINAE only works when certain errors (like the ones you have) happen in declaration part. In your case they happen in definition. You have to rewrite your code so that enable_if is used in declaration, in your case of struct extract_caller.
The code I got by fixing the problem doublep mentioned:
struct extract_caller
{
template <typename T>
static void extract(const T& val, typename enable_if<(bool)is_extractor_native<T>::val>::type * = 0)
{
extractor::extract(val);
}
template <typename T>
static void extract(const T& val, typename enable_if<!(bool)is_extractor_native<T>::val>::type * = 0)
{
extract_generic(val);
}
};
This can be used as
extract_caller::extract(string_value);
extract_caller::extract(double_value);