Template template class predicate not working in partial specialization












7















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 '18 at 6:47











  • btw, your code look a lot like the dectection idiom

    – Guillaume Racicot
    Nov 28 '18 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 '18 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 '18 at 6:52
















7















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 '18 at 6:47











  • btw, your code look a lot like the dectection idiom

    – Guillaume Racicot
    Nov 28 '18 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 '18 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 '18 at 6:52














7












7








7


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 '18 at 6:43







Curious

















asked Nov 28 '18 at 6:38









CuriousCurious

12k22571




12k22571








  • 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 '18 at 6:47











  • btw, your code look a lot like the dectection idiom

    – Guillaume Racicot
    Nov 28 '18 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 '18 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 '18 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 '18 at 6:47











  • btw, your code look a lot like the dectection idiom

    – Guillaume Racicot
    Nov 28 '18 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 '18 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 '18 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 '18 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 '18 at 6:47













btw, your code look a lot like the dectection idiom

– Guillaume Racicot
Nov 28 '18 at 6:49





btw, your code look a lot like the dectection idiom

– Guillaume Racicot
Nov 28 '18 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 '18 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 '18 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 '18 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 '18 at 6:52












2 Answers
2






active

oldest

votes


















5














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 '18 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 '18 at 7:06











  • Might be. I'm examining partial ordering at the moment to see why.

    – StoryTeller
    Nov 28 '18 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 '18 at 7:12











  • Oh I think I know why. Let me edit

    – Guillaume Racicot
    Nov 28 '18 at 7:13



















5














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 '18 at 7:46











  • @StoryTeller Because the deduction starts from std::void_t<Predicate<T>>>, std::void_t is already known.

    – llllllllll
    Nov 28 '18 at 7:48













  • Sorry, by usually I meant std:void_t<decltype(std::declval<T>().foo)>

    – StoryTeller
    Nov 28 '18 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 '18 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 '18 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',
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%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









5














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 '18 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 '18 at 7:06











  • Might be. I'm examining partial ordering at the moment to see why.

    – StoryTeller
    Nov 28 '18 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 '18 at 7:12











  • Oh I think I know why. Let me edit

    – Guillaume Racicot
    Nov 28 '18 at 7:13
















5














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 '18 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 '18 at 7:06











  • Might be. I'm examining partial ordering at the moment to see why.

    – StoryTeller
    Nov 28 '18 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 '18 at 7:12











  • Oh I think I know why. Let me edit

    – Guillaume Racicot
    Nov 28 '18 at 7:13














5












5








5







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 '18 at 7:40

























answered Nov 28 '18 at 6:58









Guillaume RacicotGuillaume Racicot

15.1k53568




15.1k53568













  • Have an upvote for the minimal change. But I deleted mine because I'm still guessing at the reasons.

    – StoryTeller
    Nov 28 '18 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 '18 at 7:06











  • Might be. I'm examining partial ordering at the moment to see why.

    – StoryTeller
    Nov 28 '18 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 '18 at 7:12











  • Oh I think I know why. Let me edit

    – Guillaume Racicot
    Nov 28 '18 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 '18 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 '18 at 7:06











  • Might be. I'm examining partial ordering at the moment to see why.

    – StoryTeller
    Nov 28 '18 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 '18 at 7:12











  • Oh I think I know why. Let me edit

    – Guillaume Racicot
    Nov 28 '18 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 '18 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 '18 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 '18 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 '18 at 7:06













Might be. I'm examining partial ordering at the moment to see why.

– StoryTeller
Nov 28 '18 at 7:08





Might be. I'm examining partial ordering at the moment to see why.

– StoryTeller
Nov 28 '18 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 '18 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 '18 at 7:12













Oh I think I know why. Let me edit

– Guillaume Racicot
Nov 28 '18 at 7:13





Oh I think I know why. Let me edit

– Guillaume Racicot
Nov 28 '18 at 7:13













5














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 '18 at 7:46











  • @StoryTeller Because the deduction starts from std::void_t<Predicate<T>>>, std::void_t is already known.

    – llllllllll
    Nov 28 '18 at 7:48













  • Sorry, by usually I meant std:void_t<decltype(std::declval<T>().foo)>

    – StoryTeller
    Nov 28 '18 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 '18 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 '18 at 7:56


















5














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 '18 at 7:46











  • @StoryTeller Because the deduction starts from std::void_t<Predicate<T>>>, std::void_t is already known.

    – llllllllll
    Nov 28 '18 at 7:48













  • Sorry, by usually I meant std:void_t<decltype(std::declval<T>().foo)>

    – StoryTeller
    Nov 28 '18 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 '18 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 '18 at 7:56
















5












5








5







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 '18 at 9:12

























answered Nov 28 '18 at 7:42









llllllllllllllllllll

13.8k41742




13.8k41742













  • So... why does it usually work with void_t? It's an alias template too.

    – StoryTeller
    Nov 28 '18 at 7:46











  • @StoryTeller Because the deduction starts from std::void_t<Predicate<T>>>, std::void_t is already known.

    – llllllllll
    Nov 28 '18 at 7:48













  • Sorry, by usually I meant std:void_t<decltype(std::declval<T>().foo)>

    – StoryTeller
    Nov 28 '18 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 '18 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 '18 at 7:56





















  • So... why does it usually work with void_t? It's an alias template too.

    – StoryTeller
    Nov 28 '18 at 7:46











  • @StoryTeller Because the deduction starts from std::void_t<Predicate<T>>>, std::void_t is already known.

    – llllllllll
    Nov 28 '18 at 7:48













  • Sorry, by usually I meant std:void_t<decltype(std::declval<T>().foo)>

    – StoryTeller
    Nov 28 '18 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 '18 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 '18 at 7:56



















So... why does it usually work with void_t? It's an alias template too.

– StoryTeller
Nov 28 '18 at 7:46





So... why does it usually work with void_t? It's an alias template too.

– StoryTeller
Nov 28 '18 at 7:46













@StoryTeller Because the deduction starts from std::void_t<Predicate<T>>>, std::void_t is already known.

– llllllllll
Nov 28 '18 at 7:48







@StoryTeller Because the deduction starts from std::void_t<Predicate<T>>>, std::void_t is already known.

– llllllllll
Nov 28 '18 at 7:48















Sorry, by usually I meant std:void_t<decltype(std::declval<T>().foo)>

– StoryTeller
Nov 28 '18 at 7:49





Sorry, by usually I meant std:void_t<decltype(std::declval<T>().foo)>

– StoryTeller
Nov 28 '18 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 '18 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 '18 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 '18 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 '18 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.




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

Contact image not getting when fetch all contact list from iPhone by CNContact

count number of partitions of a set with n elements into k subsets

A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks