Template template class predicate not working in partial specialization











up vote
7
down vote

favorite
1












I have many EnableIf traits that basically check whether the input type satisfies an interface. I was trying to create a generic Resolve trait that can be used to transform those into a boolean trait.



Something like this - https://wandbox.org/permlink/ydEMyErOoaOa60Jx



template <
template <typename...> class Predicate,
typename T,
typename = std::void_t<>>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, Predicate<T>> : std::true_type {};


Now if you have an EnableIf trait like so



template <typename T>
using EnableIfHasFoo = std::void_t<decltype(std::declval<T>().foo())>;


You can create a boolean version of that very quickly



template <typename T>
struct HasFoo : Resolve<EnableIfHasFoo, T> {};


Or the analogous variable template.



But for some reason the partial specialization is not working as expected. Resolve does not work as intended. See the output here - https://wandbox.org/permlink/ydEMyErOoaOa60Jx. The same thing implemented "manually" works - https://wandbox.org/permlink/fmcFT3kLSqyiBprm



I am resorting to manually defining the types myself. Is there a detail with partial specializations and template template arguments that I am missing?










share|improve this question




















  • 1




    I cannot find the reason, but you need the partial specialization to be Resolve<Predicate, T, std::void_t<Predicate<T>>. Then at that point, you can also remove the std::void_t in the alias.
    – Guillaume Racicot
    Nov 28 at 6:47










  • btw, your code look a lot like the dectection idiom
    – Guillaume Racicot
    Nov 28 at 6:49










  • @GuillaumeRacicot But then what is the predicate? Just decltype()? Very nice! Also, confusing why the above approach doesn't work.
    – Curious
    Nov 28 at 6:49








  • 1




    It can be an alias template to simply decltype. I don't know why, but the void_t must be in the partial specialization. I think it has to do with how partial ordering work.
    – Guillaume Racicot
    Nov 28 at 6:52















up vote
7
down vote

favorite
1












I have many EnableIf traits that basically check whether the input type satisfies an interface. I was trying to create a generic Resolve trait that can be used to transform those into a boolean trait.



Something like this - https://wandbox.org/permlink/ydEMyErOoaOa60Jx



template <
template <typename...> class Predicate,
typename T,
typename = std::void_t<>>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, Predicate<T>> : std::true_type {};


Now if you have an EnableIf trait like so



template <typename T>
using EnableIfHasFoo = std::void_t<decltype(std::declval<T>().foo())>;


You can create a boolean version of that very quickly



template <typename T>
struct HasFoo : Resolve<EnableIfHasFoo, T> {};


Or the analogous variable template.



But for some reason the partial specialization is not working as expected. Resolve does not work as intended. See the output here - https://wandbox.org/permlink/ydEMyErOoaOa60Jx. The same thing implemented "manually" works - https://wandbox.org/permlink/fmcFT3kLSqyiBprm



I am resorting to manually defining the types myself. Is there a detail with partial specializations and template template arguments that I am missing?










share|improve this question




















  • 1




    I cannot find the reason, but you need the partial specialization to be Resolve<Predicate, T, std::void_t<Predicate<T>>. Then at that point, you can also remove the std::void_t in the alias.
    – Guillaume Racicot
    Nov 28 at 6:47










  • btw, your code look a lot like the dectection idiom
    – Guillaume Racicot
    Nov 28 at 6:49










  • @GuillaumeRacicot But then what is the predicate? Just decltype()? Very nice! Also, confusing why the above approach doesn't work.
    – Curious
    Nov 28 at 6:49








  • 1




    It can be an alias template to simply decltype. I don't know why, but the void_t must be in the partial specialization. I think it has to do with how partial ordering work.
    – Guillaume Racicot
    Nov 28 at 6:52













up vote
7
down vote

favorite
1









up vote
7
down vote

favorite
1






1





I have many EnableIf traits that basically check whether the input type satisfies an interface. I was trying to create a generic Resolve trait that can be used to transform those into a boolean trait.



Something like this - https://wandbox.org/permlink/ydEMyErOoaOa60Jx



template <
template <typename...> class Predicate,
typename T,
typename = std::void_t<>>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, Predicate<T>> : std::true_type {};


Now if you have an EnableIf trait like so



template <typename T>
using EnableIfHasFoo = std::void_t<decltype(std::declval<T>().foo())>;


You can create a boolean version of that very quickly



template <typename T>
struct HasFoo : Resolve<EnableIfHasFoo, T> {};


Or the analogous variable template.



But for some reason the partial specialization is not working as expected. Resolve does not work as intended. See the output here - https://wandbox.org/permlink/ydEMyErOoaOa60Jx. The same thing implemented "manually" works - https://wandbox.org/permlink/fmcFT3kLSqyiBprm



I am resorting to manually defining the types myself. Is there a detail with partial specializations and template template arguments that I am missing?










share|improve this question















I have many EnableIf traits that basically check whether the input type satisfies an interface. I was trying to create a generic Resolve trait that can be used to transform those into a boolean trait.



Something like this - https://wandbox.org/permlink/ydEMyErOoaOa60Jx



template <
template <typename...> class Predicate,
typename T,
typename = std::void_t<>>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, Predicate<T>> : std::true_type {};


Now if you have an EnableIf trait like so



template <typename T>
using EnableIfHasFoo = std::void_t<decltype(std::declval<T>().foo())>;


You can create a boolean version of that very quickly



template <typename T>
struct HasFoo : Resolve<EnableIfHasFoo, T> {};


Or the analogous variable template.



But for some reason the partial specialization is not working as expected. Resolve does not work as intended. See the output here - https://wandbox.org/permlink/ydEMyErOoaOa60Jx. The same thing implemented "manually" works - https://wandbox.org/permlink/fmcFT3kLSqyiBprm



I am resorting to manually defining the types myself. Is there a detail with partial specializations and template template arguments that I am missing?







c++ templates c++17 sfinae enable-if






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 28 at 6:43

























asked Nov 28 at 6:38









Curious

11.8k22470




11.8k22470








  • 1




    I cannot find the reason, but you need the partial specialization to be Resolve<Predicate, T, std::void_t<Predicate<T>>. Then at that point, you can also remove the std::void_t in the alias.
    – Guillaume Racicot
    Nov 28 at 6:47










  • btw, your code look a lot like the dectection idiom
    – Guillaume Racicot
    Nov 28 at 6:49










  • @GuillaumeRacicot But then what is the predicate? Just decltype()? Very nice! Also, confusing why the above approach doesn't work.
    – Curious
    Nov 28 at 6:49








  • 1




    It can be an alias template to simply decltype. I don't know why, but the void_t must be in the partial specialization. I think it has to do with how partial ordering work.
    – Guillaume Racicot
    Nov 28 at 6:52














  • 1




    I cannot find the reason, but you need the partial specialization to be Resolve<Predicate, T, std::void_t<Predicate<T>>. Then at that point, you can also remove the std::void_t in the alias.
    – Guillaume Racicot
    Nov 28 at 6:47










  • btw, your code look a lot like the dectection idiom
    – Guillaume Racicot
    Nov 28 at 6:49










  • @GuillaumeRacicot But then what is the predicate? Just decltype()? Very nice! Also, confusing why the above approach doesn't work.
    – Curious
    Nov 28 at 6:49








  • 1




    It can be an alias template to simply decltype. I don't know why, but the void_t must be in the partial specialization. I think it has to do with how partial ordering work.
    – Guillaume Racicot
    Nov 28 at 6:52








1




1




I cannot find the reason, but you need the partial specialization to be Resolve<Predicate, T, std::void_t<Predicate<T>>. Then at that point, you can also remove the std::void_t in the alias.
– Guillaume Racicot
Nov 28 at 6:47




I cannot find the reason, but you need the partial specialization to be Resolve<Predicate, T, std::void_t<Predicate<T>>. Then at that point, you can also remove the std::void_t in the alias.
– Guillaume Racicot
Nov 28 at 6:47












btw, your code look a lot like the dectection idiom
– Guillaume Racicot
Nov 28 at 6:49




btw, your code look a lot like the dectection idiom
– Guillaume Racicot
Nov 28 at 6:49












@GuillaumeRacicot But then what is the predicate? Just decltype()? Very nice! Also, confusing why the above approach doesn't work.
– Curious
Nov 28 at 6:49






@GuillaumeRacicot But then what is the predicate? Just decltype()? Very nice! Also, confusing why the above approach doesn't work.
– Curious
Nov 28 at 6:49






1




1




It can be an alias template to simply decltype. I don't know why, but the void_t must be in the partial specialization. I think it has to do with how partial ordering work.
– Guillaume Racicot
Nov 28 at 6:52




It can be an alias template to simply decltype. I don't know why, but the void_t must be in the partial specialization. I think it has to do with how partial ordering work.
– Guillaume Racicot
Nov 28 at 6:52












2 Answers
2






active

oldest

votes

















up vote
5
down vote













I cannot find the exact reason why your example don't work. If you want to dig more into the details of std::void_t, here's an interesting explanation



Even if I cannot explain it in depth, I would like to add another reliable syntax that is used in the detection idiom.



template<
template <typename...> class Predicate,
typename T,
typename = void>
struct Resolve : std::false_type {};

template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, std::void_t<Predicate<T>>> : std::true_type {};

template <typename T>
using EnableIfHasFoo = decltype(std::declval<T>().foo());


live on compiler explorer






share|improve this answer























  • Have an upvote for the minimal change. But I deleted mine because I'm still guessing at the reasons.
    – StoryTeller
    Nov 28 at 7:02










  • @StoryTeller Thanks. I haven't found the reason either. It might be because the rule doesn't make the specialization more specialized.
    – Guillaume Racicot
    Nov 28 at 7:06










  • Might be. I'm examining partial ordering at the moment to see why.
    – StoryTeller
    Nov 28 at 7:08










  • @StoryTeller I added a link to another answer that explains void_t. However, even that answer don't seem to explain your case completely.
    – Guillaume Racicot
    Nov 28 at 7:12










  • Oh I think I know why. Let me edit
    – Guillaume Racicot
    Nov 28 at 7:13


















up vote
5
down vote













The reason why your approach will fail is that Predicate<T>> in the third template parameter is not a non-deduced context. This causes the deduction to directly fail (see [temp.alias]/2), instead of using the deduced template arguments from elsewhere as in a non-deduced context.



You can wrap your Predicate<T>> to a non-deduced context to make it work:



template<class T>
struct identity {
using type = T;
};

template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, typename identity<Predicate<T>>::type> : std::true_type {};


Live Demo



Because inside a non-deduced context, the deduction won't happen for Predicate<T> part, instead, it will use the Predicate and T obtained from elsewhere.



As for why the usual detection-idiom (see Guillaume Racicot's answer) will work, it is because std::void_t as a template alias, will be replaced by void in deduction phase (see [temp.alias]/2), thus no deduction will happen.



Here are some examples to illustrate it more clearly:



template<class T>
using always_int = int;

template<template<class> class TT>
struct deductor {};

template<template<class> class TT, class T>
void foo(T, deductor<TT>) {}

template<template<class> class TT, class T>
void bar(T, deductor<TT>, TT<T>) {}

template<class T>
void baz(T, always_int<T>) {}

int main() {
// ok, both T and TT are deduced
foo(0, deductor<always_int>{});

// ERROR, TT<T> is NOT a non-deduced context, deduction failure
bar(0, deductor<always_int>{}, 0);

// ok, T is deduced, always_int<T> is replaced by int so no deduction
baz(0, 0);
}





share|improve this answer























  • So... why does it usually work with void_t? It's an alias template too.
    – StoryTeller
    Nov 28 at 7:46










  • @StoryTeller Because the deduction starts from std::void_t<Predicate<T>>>, std::void_t is already known.
    – liliscent
    Nov 28 at 7:48












  • Sorry, by usually I meant std:void_t<decltype(std::declval<T>().foo)>
    – StoryTeller
    Nov 28 at 7:49






  • 1




    Yeah, it is (should really print that bullet list). But shouldn't the equivalence of alias template specializations make this moot? I.e., it should be as though the OP wrote directly std::void_t<declval<...>(...).foo>, no? Sorry for flood of questions, I was just staring at these passages for too long.
    – StoryTeller
    Nov 28 at 7:54






  • 1




    Oh. no wait. I see now. Great answer! That void_t we hide is failing immediately because the specialization needs to be replaced by its equivalent type!
    – StoryTeller
    Nov 28 at 7:56













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',
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%2f53513525%2ftemplate-template-class-predicate-not-working-in-partial-specialization%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








up vote
5
down vote













I cannot find the exact reason why your example don't work. If you want to dig more into the details of std::void_t, here's an interesting explanation



Even if I cannot explain it in depth, I would like to add another reliable syntax that is used in the detection idiom.



template<
template <typename...> class Predicate,
typename T,
typename = void>
struct Resolve : std::false_type {};

template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, std::void_t<Predicate<T>>> : std::true_type {};

template <typename T>
using EnableIfHasFoo = decltype(std::declval<T>().foo());


live on compiler explorer






share|improve this answer























  • Have an upvote for the minimal change. But I deleted mine because I'm still guessing at the reasons.
    – StoryTeller
    Nov 28 at 7:02










  • @StoryTeller Thanks. I haven't found the reason either. It might be because the rule doesn't make the specialization more specialized.
    – Guillaume Racicot
    Nov 28 at 7:06










  • Might be. I'm examining partial ordering at the moment to see why.
    – StoryTeller
    Nov 28 at 7:08










  • @StoryTeller I added a link to another answer that explains void_t. However, even that answer don't seem to explain your case completely.
    – Guillaume Racicot
    Nov 28 at 7:12










  • Oh I think I know why. Let me edit
    – Guillaume Racicot
    Nov 28 at 7:13















up vote
5
down vote













I cannot find the exact reason why your example don't work. If you want to dig more into the details of std::void_t, here's an interesting explanation



Even if I cannot explain it in depth, I would like to add another reliable syntax that is used in the detection idiom.



template<
template <typename...> class Predicate,
typename T,
typename = void>
struct Resolve : std::false_type {};

template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, std::void_t<Predicate<T>>> : std::true_type {};

template <typename T>
using EnableIfHasFoo = decltype(std::declval<T>().foo());


live on compiler explorer






share|improve this answer























  • Have an upvote for the minimal change. But I deleted mine because I'm still guessing at the reasons.
    – StoryTeller
    Nov 28 at 7:02










  • @StoryTeller Thanks. I haven't found the reason either. It might be because the rule doesn't make the specialization more specialized.
    – Guillaume Racicot
    Nov 28 at 7:06










  • Might be. I'm examining partial ordering at the moment to see why.
    – StoryTeller
    Nov 28 at 7:08










  • @StoryTeller I added a link to another answer that explains void_t. However, even that answer don't seem to explain your case completely.
    – Guillaume Racicot
    Nov 28 at 7:12










  • Oh I think I know why. Let me edit
    – Guillaume Racicot
    Nov 28 at 7:13













up vote
5
down vote










up vote
5
down vote









I cannot find the exact reason why your example don't work. If you want to dig more into the details of std::void_t, here's an interesting explanation



Even if I cannot explain it in depth, I would like to add another reliable syntax that is used in the detection idiom.



template<
template <typename...> class Predicate,
typename T,
typename = void>
struct Resolve : std::false_type {};

template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, std::void_t<Predicate<T>>> : std::true_type {};

template <typename T>
using EnableIfHasFoo = decltype(std::declval<T>().foo());


live on compiler explorer






share|improve this answer














I cannot find the exact reason why your example don't work. If you want to dig more into the details of std::void_t, here's an interesting explanation



Even if I cannot explain it in depth, I would like to add another reliable syntax that is used in the detection idiom.



template<
template <typename...> class Predicate,
typename T,
typename = void>
struct Resolve : std::false_type {};

template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, std::void_t<Predicate<T>>> : std::true_type {};

template <typename T>
using EnableIfHasFoo = decltype(std::declval<T>().foo());


live on compiler explorer







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 28 at 7:40

























answered Nov 28 at 6:58









Guillaume Racicot

13.1k43163




13.1k43163












  • Have an upvote for the minimal change. But I deleted mine because I'm still guessing at the reasons.
    – StoryTeller
    Nov 28 at 7:02










  • @StoryTeller Thanks. I haven't found the reason either. It might be because the rule doesn't make the specialization more specialized.
    – Guillaume Racicot
    Nov 28 at 7:06










  • Might be. I'm examining partial ordering at the moment to see why.
    – StoryTeller
    Nov 28 at 7:08










  • @StoryTeller I added a link to another answer that explains void_t. However, even that answer don't seem to explain your case completely.
    – Guillaume Racicot
    Nov 28 at 7:12










  • Oh I think I know why. Let me edit
    – Guillaume Racicot
    Nov 28 at 7:13


















  • Have an upvote for the minimal change. But I deleted mine because I'm still guessing at the reasons.
    – StoryTeller
    Nov 28 at 7:02










  • @StoryTeller Thanks. I haven't found the reason either. It might be because the rule doesn't make the specialization more specialized.
    – Guillaume Racicot
    Nov 28 at 7:06










  • Might be. I'm examining partial ordering at the moment to see why.
    – StoryTeller
    Nov 28 at 7:08










  • @StoryTeller I added a link to another answer that explains void_t. However, even that answer don't seem to explain your case completely.
    – Guillaume Racicot
    Nov 28 at 7:12










  • Oh I think I know why. Let me edit
    – Guillaume Racicot
    Nov 28 at 7:13
















Have an upvote for the minimal change. But I deleted mine because I'm still guessing at the reasons.
– StoryTeller
Nov 28 at 7:02




Have an upvote for the minimal change. But I deleted mine because I'm still guessing at the reasons.
– StoryTeller
Nov 28 at 7:02












@StoryTeller Thanks. I haven't found the reason either. It might be because the rule doesn't make the specialization more specialized.
– Guillaume Racicot
Nov 28 at 7:06




@StoryTeller Thanks. I haven't found the reason either. It might be because the rule doesn't make the specialization more specialized.
– Guillaume Racicot
Nov 28 at 7:06












Might be. I'm examining partial ordering at the moment to see why.
– StoryTeller
Nov 28 at 7:08




Might be. I'm examining partial ordering at the moment to see why.
– StoryTeller
Nov 28 at 7:08












@StoryTeller I added a link to another answer that explains void_t. However, even that answer don't seem to explain your case completely.
– Guillaume Racicot
Nov 28 at 7:12




@StoryTeller I added a link to another answer that explains void_t. However, even that answer don't seem to explain your case completely.
– Guillaume Racicot
Nov 28 at 7:12












Oh I think I know why. Let me edit
– Guillaume Racicot
Nov 28 at 7:13




Oh I think I know why. Let me edit
– Guillaume Racicot
Nov 28 at 7:13












up vote
5
down vote













The reason why your approach will fail is that Predicate<T>> in the third template parameter is not a non-deduced context. This causes the deduction to directly fail (see [temp.alias]/2), instead of using the deduced template arguments from elsewhere as in a non-deduced context.



You can wrap your Predicate<T>> to a non-deduced context to make it work:



template<class T>
struct identity {
using type = T;
};

template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, typename identity<Predicate<T>>::type> : std::true_type {};


Live Demo



Because inside a non-deduced context, the deduction won't happen for Predicate<T> part, instead, it will use the Predicate and T obtained from elsewhere.



As for why the usual detection-idiom (see Guillaume Racicot's answer) will work, it is because std::void_t as a template alias, will be replaced by void in deduction phase (see [temp.alias]/2), thus no deduction will happen.



Here are some examples to illustrate it more clearly:



template<class T>
using always_int = int;

template<template<class> class TT>
struct deductor {};

template<template<class> class TT, class T>
void foo(T, deductor<TT>) {}

template<template<class> class TT, class T>
void bar(T, deductor<TT>, TT<T>) {}

template<class T>
void baz(T, always_int<T>) {}

int main() {
// ok, both T and TT are deduced
foo(0, deductor<always_int>{});

// ERROR, TT<T> is NOT a non-deduced context, deduction failure
bar(0, deductor<always_int>{}, 0);

// ok, T is deduced, always_int<T> is replaced by int so no deduction
baz(0, 0);
}





share|improve this answer























  • So... why does it usually work with void_t? It's an alias template too.
    – StoryTeller
    Nov 28 at 7:46










  • @StoryTeller Because the deduction starts from std::void_t<Predicate<T>>>, std::void_t is already known.
    – liliscent
    Nov 28 at 7:48












  • Sorry, by usually I meant std:void_t<decltype(std::declval<T>().foo)>
    – StoryTeller
    Nov 28 at 7:49






  • 1




    Yeah, it is (should really print that bullet list). But shouldn't the equivalence of alias template specializations make this moot? I.e., it should be as though the OP wrote directly std::void_t<declval<...>(...).foo>, no? Sorry for flood of questions, I was just staring at these passages for too long.
    – StoryTeller
    Nov 28 at 7:54






  • 1




    Oh. no wait. I see now. Great answer! That void_t we hide is failing immediately because the specialization needs to be replaced by its equivalent type!
    – StoryTeller
    Nov 28 at 7:56

















up vote
5
down vote













The reason why your approach will fail is that Predicate<T>> in the third template parameter is not a non-deduced context. This causes the deduction to directly fail (see [temp.alias]/2), instead of using the deduced template arguments from elsewhere as in a non-deduced context.



You can wrap your Predicate<T>> to a non-deduced context to make it work:



template<class T>
struct identity {
using type = T;
};

template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, typename identity<Predicate<T>>::type> : std::true_type {};


Live Demo



Because inside a non-deduced context, the deduction won't happen for Predicate<T> part, instead, it will use the Predicate and T obtained from elsewhere.



As for why the usual detection-idiom (see Guillaume Racicot's answer) will work, it is because std::void_t as a template alias, will be replaced by void in deduction phase (see [temp.alias]/2), thus no deduction will happen.



Here are some examples to illustrate it more clearly:



template<class T>
using always_int = int;

template<template<class> class TT>
struct deductor {};

template<template<class> class TT, class T>
void foo(T, deductor<TT>) {}

template<template<class> class TT, class T>
void bar(T, deductor<TT>, TT<T>) {}

template<class T>
void baz(T, always_int<T>) {}

int main() {
// ok, both T and TT are deduced
foo(0, deductor<always_int>{});

// ERROR, TT<T> is NOT a non-deduced context, deduction failure
bar(0, deductor<always_int>{}, 0);

// ok, T is deduced, always_int<T> is replaced by int so no deduction
baz(0, 0);
}





share|improve this answer























  • So... why does it usually work with void_t? It's an alias template too.
    – StoryTeller
    Nov 28 at 7:46










  • @StoryTeller Because the deduction starts from std::void_t<Predicate<T>>>, std::void_t is already known.
    – liliscent
    Nov 28 at 7:48












  • Sorry, by usually I meant std:void_t<decltype(std::declval<T>().foo)>
    – StoryTeller
    Nov 28 at 7:49






  • 1




    Yeah, it is (should really print that bullet list). But shouldn't the equivalence of alias template specializations make this moot? I.e., it should be as though the OP wrote directly std::void_t<declval<...>(...).foo>, no? Sorry for flood of questions, I was just staring at these passages for too long.
    – StoryTeller
    Nov 28 at 7:54






  • 1




    Oh. no wait. I see now. Great answer! That void_t we hide is failing immediately because the specialization needs to be replaced by its equivalent type!
    – StoryTeller
    Nov 28 at 7:56















up vote
5
down vote










up vote
5
down vote









The reason why your approach will fail is that Predicate<T>> in the third template parameter is not a non-deduced context. This causes the deduction to directly fail (see [temp.alias]/2), instead of using the deduced template arguments from elsewhere as in a non-deduced context.



You can wrap your Predicate<T>> to a non-deduced context to make it work:



template<class T>
struct identity {
using type = T;
};

template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, typename identity<Predicate<T>>::type> : std::true_type {};


Live Demo



Because inside a non-deduced context, the deduction won't happen for Predicate<T> part, instead, it will use the Predicate and T obtained from elsewhere.



As for why the usual detection-idiom (see Guillaume Racicot's answer) will work, it is because std::void_t as a template alias, will be replaced by void in deduction phase (see [temp.alias]/2), thus no deduction will happen.



Here are some examples to illustrate it more clearly:



template<class T>
using always_int = int;

template<template<class> class TT>
struct deductor {};

template<template<class> class TT, class T>
void foo(T, deductor<TT>) {}

template<template<class> class TT, class T>
void bar(T, deductor<TT>, TT<T>) {}

template<class T>
void baz(T, always_int<T>) {}

int main() {
// ok, both T and TT are deduced
foo(0, deductor<always_int>{});

// ERROR, TT<T> is NOT a non-deduced context, deduction failure
bar(0, deductor<always_int>{}, 0);

// ok, T is deduced, always_int<T> is replaced by int so no deduction
baz(0, 0);
}





share|improve this answer














The reason why your approach will fail is that Predicate<T>> in the third template parameter is not a non-deduced context. This causes the deduction to directly fail (see [temp.alias]/2), instead of using the deduced template arguments from elsewhere as in a non-deduced context.



You can wrap your Predicate<T>> to a non-deduced context to make it work:



template<class T>
struct identity {
using type = T;
};

template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, typename identity<Predicate<T>>::type> : std::true_type {};


Live Demo



Because inside a non-deduced context, the deduction won't happen for Predicate<T> part, instead, it will use the Predicate and T obtained from elsewhere.



As for why the usual detection-idiom (see Guillaume Racicot's answer) will work, it is because std::void_t as a template alias, will be replaced by void in deduction phase (see [temp.alias]/2), thus no deduction will happen.



Here are some examples to illustrate it more clearly:



template<class T>
using always_int = int;

template<template<class> class TT>
struct deductor {};

template<template<class> class TT, class T>
void foo(T, deductor<TT>) {}

template<template<class> class TT, class T>
void bar(T, deductor<TT>, TT<T>) {}

template<class T>
void baz(T, always_int<T>) {}

int main() {
// ok, both T and TT are deduced
foo(0, deductor<always_int>{});

// ERROR, TT<T> is NOT a non-deduced context, deduction failure
bar(0, deductor<always_int>{}, 0);

// ok, T is deduced, always_int<T> is replaced by int so no deduction
baz(0, 0);
}






share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 28 at 9:12

























answered Nov 28 at 7:42









liliscent

12.7k41639




12.7k41639












  • So... why does it usually work with void_t? It's an alias template too.
    – StoryTeller
    Nov 28 at 7:46










  • @StoryTeller Because the deduction starts from std::void_t<Predicate<T>>>, std::void_t is already known.
    – liliscent
    Nov 28 at 7:48












  • Sorry, by usually I meant std:void_t<decltype(std::declval<T>().foo)>
    – StoryTeller
    Nov 28 at 7:49






  • 1




    Yeah, it is (should really print that bullet list). But shouldn't the equivalence of alias template specializations make this moot? I.e., it should be as though the OP wrote directly std::void_t<declval<...>(...).foo>, no? Sorry for flood of questions, I was just staring at these passages for too long.
    – StoryTeller
    Nov 28 at 7:54






  • 1




    Oh. no wait. I see now. Great answer! That void_t we hide is failing immediately because the specialization needs to be replaced by its equivalent type!
    – StoryTeller
    Nov 28 at 7:56




















  • So... why does it usually work with void_t? It's an alias template too.
    – StoryTeller
    Nov 28 at 7:46










  • @StoryTeller Because the deduction starts from std::void_t<Predicate<T>>>, std::void_t is already known.
    – liliscent
    Nov 28 at 7:48












  • Sorry, by usually I meant std:void_t<decltype(std::declval<T>().foo)>
    – StoryTeller
    Nov 28 at 7:49






  • 1




    Yeah, it is (should really print that bullet list). But shouldn't the equivalence of alias template specializations make this moot? I.e., it should be as though the OP wrote directly std::void_t<declval<...>(...).foo>, no? Sorry for flood of questions, I was just staring at these passages for too long.
    – StoryTeller
    Nov 28 at 7:54






  • 1




    Oh. no wait. I see now. Great answer! That void_t we hide is failing immediately because the specialization needs to be replaced by its equivalent type!
    – StoryTeller
    Nov 28 at 7:56


















So... why does it usually work with void_t? It's an alias template too.
– StoryTeller
Nov 28 at 7:46




So... why does it usually work with void_t? It's an alias template too.
– StoryTeller
Nov 28 at 7:46












@StoryTeller Because the deduction starts from std::void_t<Predicate<T>>>, std::void_t is already known.
– liliscent
Nov 28 at 7:48






@StoryTeller Because the deduction starts from std::void_t<Predicate<T>>>, std::void_t is already known.
– liliscent
Nov 28 at 7:48














Sorry, by usually I meant std:void_t<decltype(std::declval<T>().foo)>
– StoryTeller
Nov 28 at 7:49




Sorry, by usually I meant std:void_t<decltype(std::declval<T>().foo)>
– StoryTeller
Nov 28 at 7:49




1




1




Yeah, it is (should really print that bullet list). But shouldn't the equivalence of alias template specializations make this moot? I.e., it should be as though the OP wrote directly std::void_t<declval<...>(...).foo>, no? Sorry for flood of questions, I was just staring at these passages for too long.
– StoryTeller
Nov 28 at 7:54




Yeah, it is (should really print that bullet list). But shouldn't the equivalence of alias template specializations make this moot? I.e., it should be as though the OP wrote directly std::void_t<declval<...>(...).foo>, no? Sorry for flood of questions, I was just staring at these passages for too long.
– StoryTeller
Nov 28 at 7:54




1




1




Oh. no wait. I see now. Great answer! That void_t we hide is failing immediately because the specialization needs to be replaced by its equivalent type!
– StoryTeller
Nov 28 at 7:56






Oh. no wait. I see now. Great answer! That void_t we hide is failing immediately because the specialization needs to be replaced by its equivalent type!
– StoryTeller
Nov 28 at 7:56




















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.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • 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%2f53513525%2ftemplate-template-class-predicate-not-working-in-partial-specialization%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

AnyDesk - Fatal Program Failure

How to calibrate 16:9 built-in touch-screen to a 4:3 resolution?

QoS: MAC-Priority for clients behind a repeater