Consider the following test code:
// Preprocessor
#include <iostream>
#include <type_traits>
// Structure with no type alias
template <class T>
struct invalid {
};
// Structure with a type alias
template <class T>
struct valid {
    using type = T;
};
// Traits getting the type of the first type
template <class T, class... Args>
struct traits {
    using type = typename T::type;
};
// One argument function
template <class T, class = typename traits<T>::type>
void function(T) {
    std::cout << "function(T)" << std::endl;
}
// Two arguments function
template <class T, class U, class = traits<T, U>::type>
void function(T, U) {
    std::cout << "function(T, U)" << std::endl;
}
// When function can be called on all arguments
template <
    class... Args,
    class = decltype(function(std::declval<Args>()...))
>
void sfinae(Args&&... args) {
    function(std::forward<Args>(args)...);
    std::cout << "sfinae(Args&&...)" << std::endl;
}
// When function can be called on all arguments except the first one
template <
    class T,
    class... Args,
    class = decltype(function(std::declval<Args>()...))
>
void sfinae(const invalid<T>&, Args&&... args) {
    function(std::forward<Args>(args)...);
    std::cout << "sfinae(const invalid<T>&, Args&&...)" << std::endl;
}
// Main function
int main(int argc, char* argv[]) {
    valid<int> v;
    invalid<int> i;
    sfinae(v);
    sfinae(i, v);
    return 0;
}
The code involves:
- a structure 
invalidthat has no::type - a structure 
validthat has a::type - a structure 
traitsthat defines::typeasT::type - an overloaded 
functionwhich should work only if the type of the first argument is such thattraits<T>::typeis defined - an overloaded 
sfinaefunction that should be able to callfunctioneven if the first argument isinvalid 
However, the SFINAE mechanism does not seem to work in this instance, and I am not sure to understand why. The error is the following:
sfinae_problem_make.cpp:19:30: error: no type named 'type' in 'invalid<int>'
    using type = typename T::type;
                 ~~~~~~~~~~~~^~~~
sfinae_problem_make.cpp:29:46: note: in instantiation of template class 'traits<invalid<int>, valid<int> >' requested here
template <class T, class U, class = typename traits<T, U>::type>
                                             ^
sfinae_problem_make.cpp:30:6: note: in instantiation of default argument for 'function<invalid<int>, valid<int> >' required here
void function(T, U) {
     ^~~~~~~~~~~~~~~~
sfinae_problem_make.cpp:37:22: note: while substituting deduced template arguments into function template 'function' [with T = invalid<int>, U = valid<int>, $2 = (no value)]
    class = decltype(function(std::declval<Args>()...))
                     ^
sfinae_problem_make.cpp:39:6: note: in instantiation of default argument for 'sfinae<invalid<int> &, valid<int> &>' required here
void sfinae(Args&&... args) {
     ^~~~~~~~~~~~~~~~~~~~~~~~
sfinae_problem_make.cpp:60:5: note: while substituting deduced template arguments into function template 'sfinae' [with Args = <invalid<int> &, valid<int> &>, $1 = (no value)]
    sfinae(i, v);
The very surprising thing is that if traits is removed from the problem:
// Preprocessor
#include <iostream>
#include <type_traits>
// Structure with no type alias
template <class T>
struct invalid {
};
// Structure with a type alias
template <class T>
struct valid {
    using type = T;
};
// Traits getting the type of the first type
template <class T, class... Args>
struct traits {
    using type = typename T::type;
};
// One argument function
template <class T, class = typename T::type>
void function(T) {
    std::cout << "function(T)" << std::endl;
}
// Two arguments function
template <class T, class U, class = typename T::type>
void function(T, U) {
    std::cout << "function(T, U)" << std::endl;
}
// When function can be called on all arguments
template <
    class... Args,
    class = decltype(function(std::declval<Args>()...))
>
void sfinae(Args&&... args) {
    function(std::forward<Args>(args)...);
    std::cout << "sfinae(Args&&...)" << std::endl;
}
// When function can be called on all arguments except the first one
template <
    class T,
    class... Args,
    class = decltype(function(std::declval<Args>()...))
>
void sfinae(const invalid<T>&, Args&&... args) {
    function(std::forward<Args>(args)...);
    std::cout << "sfinae(const invalid<T>&, Args&&...)" << std::endl;
}
// Main function
int main(int argc, char* argv[]) {
    valid<int> v;
    invalid<int> i;
    sfinae(v);
    sfinae(i, v);
    return 0;
}
then it works as expected and outputs:
function(T)
sfinae(Args&&...)
function(T)
sfinae(const invalid<T>&, Args&&...)
Question: Why the first version does not work, and is there a way to make it work with the intermediary type traits?
Aucun commentaire:
Enregistrer un commentaire