SFINAE works with deduction but fails with substitution





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}







7















Consider the following MCVE



struct A {};

template<class T>
void test(T, T) {
}

template<class T>
class Wrapper {
using type = typename T::type;
};

template<class T>
void test(Wrapper<T>, Wrapper<T>) {
}

int main() {
A a, b;
test(a, b); // works
test<A>(a, b); // doesn't work
return 0;
}


Here test(a, b); works and test<A>(a, b); fails with:



<source>:11:30: error: no type named 'type' in 'A'
using type = typename T::type;
~~~~~~~~~~~~^~~~
<source>:23:13: note: in instantiation of template class 'Wrap<A>' requested here
test<A>(a, b); // doesn't work
^
<source>:23:5: note: while substituting deduced template arguments into function template 'test' [with T = A]
test<A>(a, b); // doesn't work


LIVE DEMO



Question: Why is that? Shouldn't SFINAE work during substitution? Yet here it seems to work during deduction only.










share|improve this question























  • In test(a, b);, T cannot be deduced for the second overload of test, so this overload is discarded before the instantiation of Wrapper<A>, and so the list of candidates only contains the first test, which is why this works. In the second case, the second overload is valid candidate since T is explicitly provided, thus implying the instantiation of Wrapper<A> which fails.

    – Holt
    2 hours ago











  • Just curious: what is your intent here? To select different overloads based on the existence of the ::type member type?

    – L. F.
    2 hours ago











  • @LF The intent was to restrict the template to Wrapper<T> types, it seemed like it worked like SFINAE sometimes but apparently it didn't.

    – rustyx
    1 hour ago


















7















Consider the following MCVE



struct A {};

template<class T>
void test(T, T) {
}

template<class T>
class Wrapper {
using type = typename T::type;
};

template<class T>
void test(Wrapper<T>, Wrapper<T>) {
}

int main() {
A a, b;
test(a, b); // works
test<A>(a, b); // doesn't work
return 0;
}


Here test(a, b); works and test<A>(a, b); fails with:



<source>:11:30: error: no type named 'type' in 'A'
using type = typename T::type;
~~~~~~~~~~~~^~~~
<source>:23:13: note: in instantiation of template class 'Wrap<A>' requested here
test<A>(a, b); // doesn't work
^
<source>:23:5: note: while substituting deduced template arguments into function template 'test' [with T = A]
test<A>(a, b); // doesn't work


LIVE DEMO



Question: Why is that? Shouldn't SFINAE work during substitution? Yet here it seems to work during deduction only.










share|improve this question























  • In test(a, b);, T cannot be deduced for the second overload of test, so this overload is discarded before the instantiation of Wrapper<A>, and so the list of candidates only contains the first test, which is why this works. In the second case, the second overload is valid candidate since T is explicitly provided, thus implying the instantiation of Wrapper<A> which fails.

    – Holt
    2 hours ago











  • Just curious: what is your intent here? To select different overloads based on the existence of the ::type member type?

    – L. F.
    2 hours ago











  • @LF The intent was to restrict the template to Wrapper<T> types, it seemed like it worked like SFINAE sometimes but apparently it didn't.

    – rustyx
    1 hour ago














7












7








7


0






Consider the following MCVE



struct A {};

template<class T>
void test(T, T) {
}

template<class T>
class Wrapper {
using type = typename T::type;
};

template<class T>
void test(Wrapper<T>, Wrapper<T>) {
}

int main() {
A a, b;
test(a, b); // works
test<A>(a, b); // doesn't work
return 0;
}


Here test(a, b); works and test<A>(a, b); fails with:



<source>:11:30: error: no type named 'type' in 'A'
using type = typename T::type;
~~~~~~~~~~~~^~~~
<source>:23:13: note: in instantiation of template class 'Wrap<A>' requested here
test<A>(a, b); // doesn't work
^
<source>:23:5: note: while substituting deduced template arguments into function template 'test' [with T = A]
test<A>(a, b); // doesn't work


LIVE DEMO



Question: Why is that? Shouldn't SFINAE work during substitution? Yet here it seems to work during deduction only.










share|improve this question














Consider the following MCVE



struct A {};

template<class T>
void test(T, T) {
}

template<class T>
class Wrapper {
using type = typename T::type;
};

template<class T>
void test(Wrapper<T>, Wrapper<T>) {
}

int main() {
A a, b;
test(a, b); // works
test<A>(a, b); // doesn't work
return 0;
}


Here test(a, b); works and test<A>(a, b); fails with:



<source>:11:30: error: no type named 'type' in 'A'
using type = typename T::type;
~~~~~~~~~~~~^~~~
<source>:23:13: note: in instantiation of template class 'Wrap<A>' requested here
test<A>(a, b); // doesn't work
^
<source>:23:5: note: while substituting deduced template arguments into function template 'test' [with T = A]
test<A>(a, b); // doesn't work


LIVE DEMO



Question: Why is that? Shouldn't SFINAE work during substitution? Yet here it seems to work during deduction only.







c++ c++11 templates language-lawyer sfinae






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked 3 hours ago









rustyxrustyx

34.2k9104146




34.2k9104146













  • In test(a, b);, T cannot be deduced for the second overload of test, so this overload is discarded before the instantiation of Wrapper<A>, and so the list of candidates only contains the first test, which is why this works. In the second case, the second overload is valid candidate since T is explicitly provided, thus implying the instantiation of Wrapper<A> which fails.

    – Holt
    2 hours ago











  • Just curious: what is your intent here? To select different overloads based on the existence of the ::type member type?

    – L. F.
    2 hours ago











  • @LF The intent was to restrict the template to Wrapper<T> types, it seemed like it worked like SFINAE sometimes but apparently it didn't.

    – rustyx
    1 hour ago



















  • In test(a, b);, T cannot be deduced for the second overload of test, so this overload is discarded before the instantiation of Wrapper<A>, and so the list of candidates only contains the first test, which is why this works. In the second case, the second overload is valid candidate since T is explicitly provided, thus implying the instantiation of Wrapper<A> which fails.

    – Holt
    2 hours ago











  • Just curious: what is your intent here? To select different overloads based on the existence of the ::type member type?

    – L. F.
    2 hours ago











  • @LF The intent was to restrict the template to Wrapper<T> types, it seemed like it worked like SFINAE sometimes but apparently it didn't.

    – rustyx
    1 hour ago

















In test(a, b);, T cannot be deduced for the second overload of test, so this overload is discarded before the instantiation of Wrapper<A>, and so the list of candidates only contains the first test, which is why this works. In the second case, the second overload is valid candidate since T is explicitly provided, thus implying the instantiation of Wrapper<A> which fails.

– Holt
2 hours ago





In test(a, b);, T cannot be deduced for the second overload of test, so this overload is discarded before the instantiation of Wrapper<A>, and so the list of candidates only contains the first test, which is why this works. In the second case, the second overload is valid candidate since T is explicitly provided, thus implying the instantiation of Wrapper<A> which fails.

– Holt
2 hours ago













Just curious: what is your intent here? To select different overloads based on the existence of the ::type member type?

– L. F.
2 hours ago





Just curious: what is your intent here? To select different overloads based on the existence of the ::type member type?

– L. F.
2 hours ago













@LF The intent was to restrict the template to Wrapper<T> types, it seemed like it worked like SFINAE sometimes but apparently it didn't.

– rustyx
1 hour ago





@LF The intent was to restrict the template to Wrapper<T> types, it seemed like it worked like SFINAE sometimes but apparently it didn't.

– rustyx
1 hour ago












2 Answers
2






active

oldest

votes


















10














Self introduction



Hello everyone, I am an innocent compiler.



The first call



test(a, b);     // works


In this call, the argument type is A. Let me first consider the first overload:



template <class T>
void test(T, T);


Easy. T = A.
Now consider the second:



template <class T>
void test(Wrapper<T>, Wrapper<T>);


Hmm ... what? Wrapper<T> for A? I have to instantiate Wrapper<T> for every possible type T in the world just to make sure that a parameter of type Wrapper<T>, which might be specialized, can't be initialized with an argument of type A? Well ... I'm not going to do that ...



Hence no Wrapper<T> is instantiated. The first overload is chosen.



The second call



test<A>(a, b);  // doesn't work


test<A>? Aha, I don't have to do deduction. Let me just check the two overloads.



template <class T>
void test(T, T);


T = A. Now substitute — the signature is (A, A). Perfect.



template <class T>
void test(Wrapper<T>, Wrapper<T>);


T = A. Now subst ... Wait, I never instantiated Wrapper<A>? I can't substitute then. How can I know whether this would be a viable overload for the call? Well, I have to instantiate it first. (instantiating) Wait ...



using type = typename T::type;


A::type? Error!



Back to L. F.



Hello everyone, I am L. F. Let's review what the compiler has done.



Was the compiler innocent enough? Did he (she?) conform to the standard?
@YSC has pointed out that [temp.over]/1 says:




When a call to the name of a function or function template is written
(explicitly, or implicitly using the operator notation), template
argument deduction ([temp.deduct]) and checking of any explicit
template arguments ([temp.arg]) are performed for each function
template to find the template argument values (if any) that can be
used with that function template to instantiate a function template
specialization that can be invoked with the call arguments. For each
function template, if the argument deduction and checking succeeds,
the template-arguments (deduced and/or explicit) are used to
synthesize the declaration of a single function template
specialization which is added to the candidate functions set to be
used in overload resolution. If, for a given function template,
argument deduction fails or the synthesized function template
specialization would be ill-formed, no such function is added to the
set of candidate functions for that template.
The complete set of
candidate functions includes all the synthesized declarations and all
of the non-template overloaded functions of the same name. The
synthesized declarations are treated like any other functions in the
remainder of overload resolution, except as explicitly noted in
[over.match.best].




The missing type leads to a hard error. Read https://stackoverflow.com/a/15261234. Basically, we have two stages when determining whether template<class T> void test(Wrapper<T>, Wrapper<T>) is the desired overload:




  1. Instantiation. In this case, we (fully) instantiate Wrapper<A>. In this stage, using type = typename T::type; is problematic because A::type is nonexistent. Problems that occur in this stage are hard errors.


  2. Substitution. Since the first stage already fails, this stage is not even reached in this case. Problems that occur in this stage are subject to SFINAE.







share|improve this answer





















  • 2





    @YSC An overload has to be instantiated for its validity to be checked. I will update my answer to clarify on this point.

    – L. F.
    2 hours ago






  • 2





    @StoryTeller Well ... given an argument of type A, Wrapper<T> can't be deduced. There is no type Wrapper<T> even remotely related to an argument of type A. Since it is not even deduced, what can be instantiated?

    – L. F.
    2 hours ago








  • 1





    Sorry, what? I don't recall a non-deduced context being dependent on the argument. It's a property of the parameter.

    – StoryTeller
    2 hours ago






  • 1





    @StoryTeller "but that includes Wrapper<whatever>" - Well, that includes Wrapper<whatever> is whatever can be deduced. Since whatever cannot be deduced, this overload is discarded from the list of candidates, and Wrapper<whatever> is not instantiated.

    – Holt
    2 hours ago






  • 2





    Thanks. What's the standard article reference for "I have to instantiate Wrapper<T> for every possible type T. I'm not going to do that..."

    – rustyx
    2 hours ago



















4














I'm not a language lawyer but I don't think that defining a using type = typename T::type; inside a class is, itself, usable as SFINAE to enable/disable a function receiving an object of that class.



If you want a solution, you can apply SFINAE to the Wrapper version as follows



template<class T>
auto test(Wrapper<T>, Wrapper<T>)
-> decltype( T::type, void() )
{ }


This way, this test() function is enabled only for T types with a type type defined inside it.



In your version, is enabled for every T type but gives error when T is incompatible with Wrapper.



-- EDIT --



The OP precises and asks




My Wrapper has many more dependencies on T, it would be impractical to duplicate them all in a SFINAE expression. Isn't there a way to check if Wrapper itself can be instantiated?




As suggested by Holt, you can create a custom type traits to see if a type is a Wrapper<something> type; by example



template <typename>
struct is_wrapper : public std::false_type
{ };

template <typename T>
struct is_wrapper<Wrapper<T>> : public std::true_type
{ using type = T; };


Then you can modify the Wrapper version to receive a U type and check if U is a Wrapper<something> type



template <typename U>
std::enable_if_t<is_wrapper<U>{}> test (U, U)
{ using T = typename is_wrapper<U>::type; }


Observe that you can recover the original T type (if you need it) using the type definition inside the is_wrapper struct.



If you need a non-Wrapper version of test(), with this solution you have to explicity disable it when T is a Wrapper<something> type to avoid collision



template <typename T>
std::enable_if_t<!is_wrapper<T>{}> test(T, T)
{ }





share|improve this answer


























  • My Wrapper has many more dependencies on T, it would be impractical to duplicate them all in a SFINAE expression. Isn't there a way to check if Wrapper<T> itself can be instantiated? Besides, would really like to know why it doesn't work.

    – rustyx
    2 hours ago











  • @rustyx - thinking on it but my first idea (using std::declval<Wrapper<T>>() instead T::type inside declval()) gives the same hard error of your case... About the "why?" question, sorry, I'm not an expert.

    – max66
    2 hours ago











  • @rustyx You need to hide Wrapper<T> in a template parameter and performs tests on this template parameter using a trait, e.g., is_wrapper... And you need to disable the other overloads when T is not a wrapper.

    – Holt
    2 hours ago











  • @Holt - do you mind if I add an example of your solution? Or do you prefer add an answer yourself?

    – max66
    2 hours ago











  • @max66 Feel free to add the example, here is a non-full tested snippet: godbolt.org/z/Los-fJ

    – Holt
    2 hours ago












Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55920964%2fsfinae-works-with-deduction-but-fails-with-substitution%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









10














Self introduction



Hello everyone, I am an innocent compiler.



The first call



test(a, b);     // works


In this call, the argument type is A. Let me first consider the first overload:



template <class T>
void test(T, T);


Easy. T = A.
Now consider the second:



template <class T>
void test(Wrapper<T>, Wrapper<T>);


Hmm ... what? Wrapper<T> for A? I have to instantiate Wrapper<T> for every possible type T in the world just to make sure that a parameter of type Wrapper<T>, which might be specialized, can't be initialized with an argument of type A? Well ... I'm not going to do that ...



Hence no Wrapper<T> is instantiated. The first overload is chosen.



The second call



test<A>(a, b);  // doesn't work


test<A>? Aha, I don't have to do deduction. Let me just check the two overloads.



template <class T>
void test(T, T);


T = A. Now substitute — the signature is (A, A). Perfect.



template <class T>
void test(Wrapper<T>, Wrapper<T>);


T = A. Now subst ... Wait, I never instantiated Wrapper<A>? I can't substitute then. How can I know whether this would be a viable overload for the call? Well, I have to instantiate it first. (instantiating) Wait ...



using type = typename T::type;


A::type? Error!



Back to L. F.



Hello everyone, I am L. F. Let's review what the compiler has done.



Was the compiler innocent enough? Did he (she?) conform to the standard?
@YSC has pointed out that [temp.over]/1 says:




When a call to the name of a function or function template is written
(explicitly, or implicitly using the operator notation), template
argument deduction ([temp.deduct]) and checking of any explicit
template arguments ([temp.arg]) are performed for each function
template to find the template argument values (if any) that can be
used with that function template to instantiate a function template
specialization that can be invoked with the call arguments. For each
function template, if the argument deduction and checking succeeds,
the template-arguments (deduced and/or explicit) are used to
synthesize the declaration of a single function template
specialization which is added to the candidate functions set to be
used in overload resolution. If, for a given function template,
argument deduction fails or the synthesized function template
specialization would be ill-formed, no such function is added to the
set of candidate functions for that template.
The complete set of
candidate functions includes all the synthesized declarations and all
of the non-template overloaded functions of the same name. The
synthesized declarations are treated like any other functions in the
remainder of overload resolution, except as explicitly noted in
[over.match.best].




The missing type leads to a hard error. Read https://stackoverflow.com/a/15261234. Basically, we have two stages when determining whether template<class T> void test(Wrapper<T>, Wrapper<T>) is the desired overload:




  1. Instantiation. In this case, we (fully) instantiate Wrapper<A>. In this stage, using type = typename T::type; is problematic because A::type is nonexistent. Problems that occur in this stage are hard errors.


  2. Substitution. Since the first stage already fails, this stage is not even reached in this case. Problems that occur in this stage are subject to SFINAE.







share|improve this answer





















  • 2





    @YSC An overload has to be instantiated for its validity to be checked. I will update my answer to clarify on this point.

    – L. F.
    2 hours ago






  • 2





    @StoryTeller Well ... given an argument of type A, Wrapper<T> can't be deduced. There is no type Wrapper<T> even remotely related to an argument of type A. Since it is not even deduced, what can be instantiated?

    – L. F.
    2 hours ago








  • 1





    Sorry, what? I don't recall a non-deduced context being dependent on the argument. It's a property of the parameter.

    – StoryTeller
    2 hours ago






  • 1





    @StoryTeller "but that includes Wrapper<whatever>" - Well, that includes Wrapper<whatever> is whatever can be deduced. Since whatever cannot be deduced, this overload is discarded from the list of candidates, and Wrapper<whatever> is not instantiated.

    – Holt
    2 hours ago






  • 2





    Thanks. What's the standard article reference for "I have to instantiate Wrapper<T> for every possible type T. I'm not going to do that..."

    – rustyx
    2 hours ago
















10














Self introduction



Hello everyone, I am an innocent compiler.



The first call



test(a, b);     // works


In this call, the argument type is A. Let me first consider the first overload:



template <class T>
void test(T, T);


Easy. T = A.
Now consider the second:



template <class T>
void test(Wrapper<T>, Wrapper<T>);


Hmm ... what? Wrapper<T> for A? I have to instantiate Wrapper<T> for every possible type T in the world just to make sure that a parameter of type Wrapper<T>, which might be specialized, can't be initialized with an argument of type A? Well ... I'm not going to do that ...



Hence no Wrapper<T> is instantiated. The first overload is chosen.



The second call



test<A>(a, b);  // doesn't work


test<A>? Aha, I don't have to do deduction. Let me just check the two overloads.



template <class T>
void test(T, T);


T = A. Now substitute — the signature is (A, A). Perfect.



template <class T>
void test(Wrapper<T>, Wrapper<T>);


T = A. Now subst ... Wait, I never instantiated Wrapper<A>? I can't substitute then. How can I know whether this would be a viable overload for the call? Well, I have to instantiate it first. (instantiating) Wait ...



using type = typename T::type;


A::type? Error!



Back to L. F.



Hello everyone, I am L. F. Let's review what the compiler has done.



Was the compiler innocent enough? Did he (she?) conform to the standard?
@YSC has pointed out that [temp.over]/1 says:




When a call to the name of a function or function template is written
(explicitly, or implicitly using the operator notation), template
argument deduction ([temp.deduct]) and checking of any explicit
template arguments ([temp.arg]) are performed for each function
template to find the template argument values (if any) that can be
used with that function template to instantiate a function template
specialization that can be invoked with the call arguments. For each
function template, if the argument deduction and checking succeeds,
the template-arguments (deduced and/or explicit) are used to
synthesize the declaration of a single function template
specialization which is added to the candidate functions set to be
used in overload resolution. If, for a given function template,
argument deduction fails or the synthesized function template
specialization would be ill-formed, no such function is added to the
set of candidate functions for that template.
The complete set of
candidate functions includes all the synthesized declarations and all
of the non-template overloaded functions of the same name. The
synthesized declarations are treated like any other functions in the
remainder of overload resolution, except as explicitly noted in
[over.match.best].




The missing type leads to a hard error. Read https://stackoverflow.com/a/15261234. Basically, we have two stages when determining whether template<class T> void test(Wrapper<T>, Wrapper<T>) is the desired overload:




  1. Instantiation. In this case, we (fully) instantiate Wrapper<A>. In this stage, using type = typename T::type; is problematic because A::type is nonexistent. Problems that occur in this stage are hard errors.


  2. Substitution. Since the first stage already fails, this stage is not even reached in this case. Problems that occur in this stage are subject to SFINAE.







share|improve this answer





















  • 2





    @YSC An overload has to be instantiated for its validity to be checked. I will update my answer to clarify on this point.

    – L. F.
    2 hours ago






  • 2





    @StoryTeller Well ... given an argument of type A, Wrapper<T> can't be deduced. There is no type Wrapper<T> even remotely related to an argument of type A. Since it is not even deduced, what can be instantiated?

    – L. F.
    2 hours ago








  • 1





    Sorry, what? I don't recall a non-deduced context being dependent on the argument. It's a property of the parameter.

    – StoryTeller
    2 hours ago






  • 1





    @StoryTeller "but that includes Wrapper<whatever>" - Well, that includes Wrapper<whatever> is whatever can be deduced. Since whatever cannot be deduced, this overload is discarded from the list of candidates, and Wrapper<whatever> is not instantiated.

    – Holt
    2 hours ago






  • 2





    Thanks. What's the standard article reference for "I have to instantiate Wrapper<T> for every possible type T. I'm not going to do that..."

    – rustyx
    2 hours ago














10












10








10







Self introduction



Hello everyone, I am an innocent compiler.



The first call



test(a, b);     // works


In this call, the argument type is A. Let me first consider the first overload:



template <class T>
void test(T, T);


Easy. T = A.
Now consider the second:



template <class T>
void test(Wrapper<T>, Wrapper<T>);


Hmm ... what? Wrapper<T> for A? I have to instantiate Wrapper<T> for every possible type T in the world just to make sure that a parameter of type Wrapper<T>, which might be specialized, can't be initialized with an argument of type A? Well ... I'm not going to do that ...



Hence no Wrapper<T> is instantiated. The first overload is chosen.



The second call



test<A>(a, b);  // doesn't work


test<A>? Aha, I don't have to do deduction. Let me just check the two overloads.



template <class T>
void test(T, T);


T = A. Now substitute — the signature is (A, A). Perfect.



template <class T>
void test(Wrapper<T>, Wrapper<T>);


T = A. Now subst ... Wait, I never instantiated Wrapper<A>? I can't substitute then. How can I know whether this would be a viable overload for the call? Well, I have to instantiate it first. (instantiating) Wait ...



using type = typename T::type;


A::type? Error!



Back to L. F.



Hello everyone, I am L. F. Let's review what the compiler has done.



Was the compiler innocent enough? Did he (she?) conform to the standard?
@YSC has pointed out that [temp.over]/1 says:




When a call to the name of a function or function template is written
(explicitly, or implicitly using the operator notation), template
argument deduction ([temp.deduct]) and checking of any explicit
template arguments ([temp.arg]) are performed for each function
template to find the template argument values (if any) that can be
used with that function template to instantiate a function template
specialization that can be invoked with the call arguments. For each
function template, if the argument deduction and checking succeeds,
the template-arguments (deduced and/or explicit) are used to
synthesize the declaration of a single function template
specialization which is added to the candidate functions set to be
used in overload resolution. If, for a given function template,
argument deduction fails or the synthesized function template
specialization would be ill-formed, no such function is added to the
set of candidate functions for that template.
The complete set of
candidate functions includes all the synthesized declarations and all
of the non-template overloaded functions of the same name. The
synthesized declarations are treated like any other functions in the
remainder of overload resolution, except as explicitly noted in
[over.match.best].




The missing type leads to a hard error. Read https://stackoverflow.com/a/15261234. Basically, we have two stages when determining whether template<class T> void test(Wrapper<T>, Wrapper<T>) is the desired overload:




  1. Instantiation. In this case, we (fully) instantiate Wrapper<A>. In this stage, using type = typename T::type; is problematic because A::type is nonexistent. Problems that occur in this stage are hard errors.


  2. Substitution. Since the first stage already fails, this stage is not even reached in this case. Problems that occur in this stage are subject to SFINAE.







share|improve this answer















Self introduction



Hello everyone, I am an innocent compiler.



The first call



test(a, b);     // works


In this call, the argument type is A. Let me first consider the first overload:



template <class T>
void test(T, T);


Easy. T = A.
Now consider the second:



template <class T>
void test(Wrapper<T>, Wrapper<T>);


Hmm ... what? Wrapper<T> for A? I have to instantiate Wrapper<T> for every possible type T in the world just to make sure that a parameter of type Wrapper<T>, which might be specialized, can't be initialized with an argument of type A? Well ... I'm not going to do that ...



Hence no Wrapper<T> is instantiated. The first overload is chosen.



The second call



test<A>(a, b);  // doesn't work


test<A>? Aha, I don't have to do deduction. Let me just check the two overloads.



template <class T>
void test(T, T);


T = A. Now substitute — the signature is (A, A). Perfect.



template <class T>
void test(Wrapper<T>, Wrapper<T>);


T = A. Now subst ... Wait, I never instantiated Wrapper<A>? I can't substitute then. How can I know whether this would be a viable overload for the call? Well, I have to instantiate it first. (instantiating) Wait ...



using type = typename T::type;


A::type? Error!



Back to L. F.



Hello everyone, I am L. F. Let's review what the compiler has done.



Was the compiler innocent enough? Did he (she?) conform to the standard?
@YSC has pointed out that [temp.over]/1 says:




When a call to the name of a function or function template is written
(explicitly, or implicitly using the operator notation), template
argument deduction ([temp.deduct]) and checking of any explicit
template arguments ([temp.arg]) are performed for each function
template to find the template argument values (if any) that can be
used with that function template to instantiate a function template
specialization that can be invoked with the call arguments. For each
function template, if the argument deduction and checking succeeds,
the template-arguments (deduced and/or explicit) are used to
synthesize the declaration of a single function template
specialization which is added to the candidate functions set to be
used in overload resolution. If, for a given function template,
argument deduction fails or the synthesized function template
specialization would be ill-formed, no such function is added to the
set of candidate functions for that template.
The complete set of
candidate functions includes all the synthesized declarations and all
of the non-template overloaded functions of the same name. The
synthesized declarations are treated like any other functions in the
remainder of overload resolution, except as explicitly noted in
[over.match.best].




The missing type leads to a hard error. Read https://stackoverflow.com/a/15261234. Basically, we have two stages when determining whether template<class T> void test(Wrapper<T>, Wrapper<T>) is the desired overload:




  1. Instantiation. In this case, we (fully) instantiate Wrapper<A>. In this stage, using type = typename T::type; is problematic because A::type is nonexistent. Problems that occur in this stage are hard errors.


  2. Substitution. Since the first stage already fails, this stage is not even reached in this case. Problems that occur in this stage are subject to SFINAE.








share|improve this answer














share|improve this answer



share|improve this answer








edited 2 hours ago

























answered 2 hours ago









L. F.L. F.

1,798626




1,798626








  • 2





    @YSC An overload has to be instantiated for its validity to be checked. I will update my answer to clarify on this point.

    – L. F.
    2 hours ago






  • 2





    @StoryTeller Well ... given an argument of type A, Wrapper<T> can't be deduced. There is no type Wrapper<T> even remotely related to an argument of type A. Since it is not even deduced, what can be instantiated?

    – L. F.
    2 hours ago








  • 1





    Sorry, what? I don't recall a non-deduced context being dependent on the argument. It's a property of the parameter.

    – StoryTeller
    2 hours ago






  • 1





    @StoryTeller "but that includes Wrapper<whatever>" - Well, that includes Wrapper<whatever> is whatever can be deduced. Since whatever cannot be deduced, this overload is discarded from the list of candidates, and Wrapper<whatever> is not instantiated.

    – Holt
    2 hours ago






  • 2





    Thanks. What's the standard article reference for "I have to instantiate Wrapper<T> for every possible type T. I'm not going to do that..."

    – rustyx
    2 hours ago














  • 2





    @YSC An overload has to be instantiated for its validity to be checked. I will update my answer to clarify on this point.

    – L. F.
    2 hours ago






  • 2





    @StoryTeller Well ... given an argument of type A, Wrapper<T> can't be deduced. There is no type Wrapper<T> even remotely related to an argument of type A. Since it is not even deduced, what can be instantiated?

    – L. F.
    2 hours ago








  • 1





    Sorry, what? I don't recall a non-deduced context being dependent on the argument. It's a property of the parameter.

    – StoryTeller
    2 hours ago






  • 1





    @StoryTeller "but that includes Wrapper<whatever>" - Well, that includes Wrapper<whatever> is whatever can be deduced. Since whatever cannot be deduced, this overload is discarded from the list of candidates, and Wrapper<whatever> is not instantiated.

    – Holt
    2 hours ago






  • 2





    Thanks. What's the standard article reference for "I have to instantiate Wrapper<T> for every possible type T. I'm not going to do that..."

    – rustyx
    2 hours ago








2




2





@YSC An overload has to be instantiated for its validity to be checked. I will update my answer to clarify on this point.

– L. F.
2 hours ago





@YSC An overload has to be instantiated for its validity to be checked. I will update my answer to clarify on this point.

– L. F.
2 hours ago




2




2





@StoryTeller Well ... given an argument of type A, Wrapper<T> can't be deduced. There is no type Wrapper<T> even remotely related to an argument of type A. Since it is not even deduced, what can be instantiated?

– L. F.
2 hours ago







@StoryTeller Well ... given an argument of type A, Wrapper<T> can't be deduced. There is no type Wrapper<T> even remotely related to an argument of type A. Since it is not even deduced, what can be instantiated?

– L. F.
2 hours ago






1




1





Sorry, what? I don't recall a non-deduced context being dependent on the argument. It's a property of the parameter.

– StoryTeller
2 hours ago





Sorry, what? I don't recall a non-deduced context being dependent on the argument. It's a property of the parameter.

– StoryTeller
2 hours ago




1




1





@StoryTeller "but that includes Wrapper<whatever>" - Well, that includes Wrapper<whatever> is whatever can be deduced. Since whatever cannot be deduced, this overload is discarded from the list of candidates, and Wrapper<whatever> is not instantiated.

– Holt
2 hours ago





@StoryTeller "but that includes Wrapper<whatever>" - Well, that includes Wrapper<whatever> is whatever can be deduced. Since whatever cannot be deduced, this overload is discarded from the list of candidates, and Wrapper<whatever> is not instantiated.

– Holt
2 hours ago




2




2





Thanks. What's the standard article reference for "I have to instantiate Wrapper<T> for every possible type T. I'm not going to do that..."

– rustyx
2 hours ago





Thanks. What's the standard article reference for "I have to instantiate Wrapper<T> for every possible type T. I'm not going to do that..."

– rustyx
2 hours ago













4














I'm not a language lawyer but I don't think that defining a using type = typename T::type; inside a class is, itself, usable as SFINAE to enable/disable a function receiving an object of that class.



If you want a solution, you can apply SFINAE to the Wrapper version as follows



template<class T>
auto test(Wrapper<T>, Wrapper<T>)
-> decltype( T::type, void() )
{ }


This way, this test() function is enabled only for T types with a type type defined inside it.



In your version, is enabled for every T type but gives error when T is incompatible with Wrapper.



-- EDIT --



The OP precises and asks




My Wrapper has many more dependencies on T, it would be impractical to duplicate them all in a SFINAE expression. Isn't there a way to check if Wrapper itself can be instantiated?




As suggested by Holt, you can create a custom type traits to see if a type is a Wrapper<something> type; by example



template <typename>
struct is_wrapper : public std::false_type
{ };

template <typename T>
struct is_wrapper<Wrapper<T>> : public std::true_type
{ using type = T; };


Then you can modify the Wrapper version to receive a U type and check if U is a Wrapper<something> type



template <typename U>
std::enable_if_t<is_wrapper<U>{}> test (U, U)
{ using T = typename is_wrapper<U>::type; }


Observe that you can recover the original T type (if you need it) using the type definition inside the is_wrapper struct.



If you need a non-Wrapper version of test(), with this solution you have to explicity disable it when T is a Wrapper<something> type to avoid collision



template <typename T>
std::enable_if_t<!is_wrapper<T>{}> test(T, T)
{ }





share|improve this answer


























  • My Wrapper has many more dependencies on T, it would be impractical to duplicate them all in a SFINAE expression. Isn't there a way to check if Wrapper<T> itself can be instantiated? Besides, would really like to know why it doesn't work.

    – rustyx
    2 hours ago











  • @rustyx - thinking on it but my first idea (using std::declval<Wrapper<T>>() instead T::type inside declval()) gives the same hard error of your case... About the "why?" question, sorry, I'm not an expert.

    – max66
    2 hours ago











  • @rustyx You need to hide Wrapper<T> in a template parameter and performs tests on this template parameter using a trait, e.g., is_wrapper... And you need to disable the other overloads when T is not a wrapper.

    – Holt
    2 hours ago











  • @Holt - do you mind if I add an example of your solution? Or do you prefer add an answer yourself?

    – max66
    2 hours ago











  • @max66 Feel free to add the example, here is a non-full tested snippet: godbolt.org/z/Los-fJ

    – Holt
    2 hours ago
















4














I'm not a language lawyer but I don't think that defining a using type = typename T::type; inside a class is, itself, usable as SFINAE to enable/disable a function receiving an object of that class.



If you want a solution, you can apply SFINAE to the Wrapper version as follows



template<class T>
auto test(Wrapper<T>, Wrapper<T>)
-> decltype( T::type, void() )
{ }


This way, this test() function is enabled only for T types with a type type defined inside it.



In your version, is enabled for every T type but gives error when T is incompatible with Wrapper.



-- EDIT --



The OP precises and asks




My Wrapper has many more dependencies on T, it would be impractical to duplicate them all in a SFINAE expression. Isn't there a way to check if Wrapper itself can be instantiated?




As suggested by Holt, you can create a custom type traits to see if a type is a Wrapper<something> type; by example



template <typename>
struct is_wrapper : public std::false_type
{ };

template <typename T>
struct is_wrapper<Wrapper<T>> : public std::true_type
{ using type = T; };


Then you can modify the Wrapper version to receive a U type and check if U is a Wrapper<something> type



template <typename U>
std::enable_if_t<is_wrapper<U>{}> test (U, U)
{ using T = typename is_wrapper<U>::type; }


Observe that you can recover the original T type (if you need it) using the type definition inside the is_wrapper struct.



If you need a non-Wrapper version of test(), with this solution you have to explicity disable it when T is a Wrapper<something> type to avoid collision



template <typename T>
std::enable_if_t<!is_wrapper<T>{}> test(T, T)
{ }





share|improve this answer


























  • My Wrapper has many more dependencies on T, it would be impractical to duplicate them all in a SFINAE expression. Isn't there a way to check if Wrapper<T> itself can be instantiated? Besides, would really like to know why it doesn't work.

    – rustyx
    2 hours ago











  • @rustyx - thinking on it but my first idea (using std::declval<Wrapper<T>>() instead T::type inside declval()) gives the same hard error of your case... About the "why?" question, sorry, I'm not an expert.

    – max66
    2 hours ago











  • @rustyx You need to hide Wrapper<T> in a template parameter and performs tests on this template parameter using a trait, e.g., is_wrapper... And you need to disable the other overloads when T is not a wrapper.

    – Holt
    2 hours ago











  • @Holt - do you mind if I add an example of your solution? Or do you prefer add an answer yourself?

    – max66
    2 hours ago











  • @max66 Feel free to add the example, here is a non-full tested snippet: godbolt.org/z/Los-fJ

    – Holt
    2 hours ago














4












4








4







I'm not a language lawyer but I don't think that defining a using type = typename T::type; inside a class is, itself, usable as SFINAE to enable/disable a function receiving an object of that class.



If you want a solution, you can apply SFINAE to the Wrapper version as follows



template<class T>
auto test(Wrapper<T>, Wrapper<T>)
-> decltype( T::type, void() )
{ }


This way, this test() function is enabled only for T types with a type type defined inside it.



In your version, is enabled for every T type but gives error when T is incompatible with Wrapper.



-- EDIT --



The OP precises and asks




My Wrapper has many more dependencies on T, it would be impractical to duplicate them all in a SFINAE expression. Isn't there a way to check if Wrapper itself can be instantiated?




As suggested by Holt, you can create a custom type traits to see if a type is a Wrapper<something> type; by example



template <typename>
struct is_wrapper : public std::false_type
{ };

template <typename T>
struct is_wrapper<Wrapper<T>> : public std::true_type
{ using type = T; };


Then you can modify the Wrapper version to receive a U type and check if U is a Wrapper<something> type



template <typename U>
std::enable_if_t<is_wrapper<U>{}> test (U, U)
{ using T = typename is_wrapper<U>::type; }


Observe that you can recover the original T type (if you need it) using the type definition inside the is_wrapper struct.



If you need a non-Wrapper version of test(), with this solution you have to explicity disable it when T is a Wrapper<something> type to avoid collision



template <typename T>
std::enable_if_t<!is_wrapper<T>{}> test(T, T)
{ }





share|improve this answer















I'm not a language lawyer but I don't think that defining a using type = typename T::type; inside a class is, itself, usable as SFINAE to enable/disable a function receiving an object of that class.



If you want a solution, you can apply SFINAE to the Wrapper version as follows



template<class T>
auto test(Wrapper<T>, Wrapper<T>)
-> decltype( T::type, void() )
{ }


This way, this test() function is enabled only for T types with a type type defined inside it.



In your version, is enabled for every T type but gives error when T is incompatible with Wrapper.



-- EDIT --



The OP precises and asks




My Wrapper has many more dependencies on T, it would be impractical to duplicate them all in a SFINAE expression. Isn't there a way to check if Wrapper itself can be instantiated?




As suggested by Holt, you can create a custom type traits to see if a type is a Wrapper<something> type; by example



template <typename>
struct is_wrapper : public std::false_type
{ };

template <typename T>
struct is_wrapper<Wrapper<T>> : public std::true_type
{ using type = T; };


Then you can modify the Wrapper version to receive a U type and check if U is a Wrapper<something> type



template <typename U>
std::enable_if_t<is_wrapper<U>{}> test (U, U)
{ using T = typename is_wrapper<U>::type; }


Observe that you can recover the original T type (if you need it) using the type definition inside the is_wrapper struct.



If you need a non-Wrapper version of test(), with this solution you have to explicity disable it when T is a Wrapper<something> type to avoid collision



template <typename T>
std::enable_if_t<!is_wrapper<T>{}> test(T, T)
{ }






share|improve this answer














share|improve this answer



share|improve this answer








edited 2 hours ago

























answered 3 hours ago









max66max66

40k74575




40k74575













  • My Wrapper has many more dependencies on T, it would be impractical to duplicate them all in a SFINAE expression. Isn't there a way to check if Wrapper<T> itself can be instantiated? Besides, would really like to know why it doesn't work.

    – rustyx
    2 hours ago











  • @rustyx - thinking on it but my first idea (using std::declval<Wrapper<T>>() instead T::type inside declval()) gives the same hard error of your case... About the "why?" question, sorry, I'm not an expert.

    – max66
    2 hours ago











  • @rustyx You need to hide Wrapper<T> in a template parameter and performs tests on this template parameter using a trait, e.g., is_wrapper... And you need to disable the other overloads when T is not a wrapper.

    – Holt
    2 hours ago











  • @Holt - do you mind if I add an example of your solution? Or do you prefer add an answer yourself?

    – max66
    2 hours ago











  • @max66 Feel free to add the example, here is a non-full tested snippet: godbolt.org/z/Los-fJ

    – Holt
    2 hours ago



















  • My Wrapper has many more dependencies on T, it would be impractical to duplicate them all in a SFINAE expression. Isn't there a way to check if Wrapper<T> itself can be instantiated? Besides, would really like to know why it doesn't work.

    – rustyx
    2 hours ago











  • @rustyx - thinking on it but my first idea (using std::declval<Wrapper<T>>() instead T::type inside declval()) gives the same hard error of your case... About the "why?" question, sorry, I'm not an expert.

    – max66
    2 hours ago











  • @rustyx You need to hide Wrapper<T> in a template parameter and performs tests on this template parameter using a trait, e.g., is_wrapper... And you need to disable the other overloads when T is not a wrapper.

    – Holt
    2 hours ago











  • @Holt - do you mind if I add an example of your solution? Or do you prefer add an answer yourself?

    – max66
    2 hours ago











  • @max66 Feel free to add the example, here is a non-full tested snippet: godbolt.org/z/Los-fJ

    – Holt
    2 hours ago

















My Wrapper has many more dependencies on T, it would be impractical to duplicate them all in a SFINAE expression. Isn't there a way to check if Wrapper<T> itself can be instantiated? Besides, would really like to know why it doesn't work.

– rustyx
2 hours ago





My Wrapper has many more dependencies on T, it would be impractical to duplicate them all in a SFINAE expression. Isn't there a way to check if Wrapper<T> itself can be instantiated? Besides, would really like to know why it doesn't work.

– rustyx
2 hours ago













@rustyx - thinking on it but my first idea (using std::declval<Wrapper<T>>() instead T::type inside declval()) gives the same hard error of your case... About the "why?" question, sorry, I'm not an expert.

– max66
2 hours ago





@rustyx - thinking on it but my first idea (using std::declval<Wrapper<T>>() instead T::type inside declval()) gives the same hard error of your case... About the "why?" question, sorry, I'm not an expert.

– max66
2 hours ago













@rustyx You need to hide Wrapper<T> in a template parameter and performs tests on this template parameter using a trait, e.g., is_wrapper... And you need to disable the other overloads when T is not a wrapper.

– Holt
2 hours ago





@rustyx You need to hide Wrapper<T> in a template parameter and performs tests on this template parameter using a trait, e.g., is_wrapper... And you need to disable the other overloads when T is not a wrapper.

– Holt
2 hours ago













@Holt - do you mind if I add an example of your solution? Or do you prefer add an answer yourself?

– max66
2 hours ago





@Holt - do you mind if I add an example of your solution? Or do you prefer add an answer yourself?

– max66
2 hours ago













@max66 Feel free to add the example, here is a non-full tested snippet: godbolt.org/z/Los-fJ

– Holt
2 hours ago





@max66 Feel free to add the example, here is a non-full tested snippet: godbolt.org/z/Los-fJ

– Holt
2 hours ago


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55920964%2fsfinae-works-with-deduction-but-fails-with-substitution%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

GameSpot

日野市

Tu-95轟炸機