How does short-circuiting work in std::conjunction [duplicate]












4















This question already has an answer here:




  • static_assert and class templates

    2 answers




Given the following code (https://wandbox.org/permlink/Eof3RQs49weJMWan)



#include <tuple>
#include <type_traits>
#include <utility>

template <typename T>
inline constexpr auto always_false = false;

template <typename T>
class HardError {
static_assert(always_false<T>);
};

int main() {
std::ignore = std::conjunction<std::false_type, HardError<int>>{};
}


I am trying to understand why this does not error out with std::conjunction as used above. I understand that it is meant to allow short-circuiting so this does not happen, and that this is by design.



However, I don't understand the language rules that allow this to happen. Given the implementation of std::conjunction below



template<class...> struct conjunction : std::true_type { };
template<class B1> struct conjunction<B1> : B1 { };
template<class B1, class... Bn>
struct conjunction<B1, Bn...>
: std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};


We end up inheriting from this specialization of std::conditional



template<class T, class F>
struct conditional<false, T, F> { typedef F type; };


This requires both class types for instantiation. Then how is the conjunction<Bn...> elided by the language?










share|improve this question













marked as duplicate by xskxzr, Baum mit Augen c++
Users with the  c++ badge can single-handedly close c++ questions as duplicates and reopen them as needed.

StackExchange.ready(function() {
if (StackExchange.options.isMobile) return;

$('.dupe-hammer-message-hover:not(.hover-bound)').each(function() {
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');

$hover.hover(
function() {
$hover.showInfoMessage('', {
messageElement: $msg.clone().show(),
transient: false,
position: { my: 'bottom left', at: 'top center', offsetTop: -7 },
dismissable: false,
relativeToBody: true
});
},
function() {
StackExchange.helpers.removeMessages();
}
);
});
});
Nov 18 '18 at 21:00


This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.











  • 1




    Now, add typedef HardError<int> zz; to your sample code. This will also compile without any errors. Now, try to instantiate zz, only then you get an error. Just because a class with a failed static assertion is referenced doesn't trigger the static assertion, but when you attempt to create an instance of the class. No instance of HardError gets instantiated during the unraveling of std::conjunction.
    – Sam Varshavchik
    Nov 18 '18 at 1:58












  • In addition, I think it is ill-formed, no diagnostic required, according to [temp.res]/8.1
    – xskxzr
    Nov 18 '18 at 16:55
















4















This question already has an answer here:




  • static_assert and class templates

    2 answers




Given the following code (https://wandbox.org/permlink/Eof3RQs49weJMWan)



#include <tuple>
#include <type_traits>
#include <utility>

template <typename T>
inline constexpr auto always_false = false;

template <typename T>
class HardError {
static_assert(always_false<T>);
};

int main() {
std::ignore = std::conjunction<std::false_type, HardError<int>>{};
}


I am trying to understand why this does not error out with std::conjunction as used above. I understand that it is meant to allow short-circuiting so this does not happen, and that this is by design.



However, I don't understand the language rules that allow this to happen. Given the implementation of std::conjunction below



template<class...> struct conjunction : std::true_type { };
template<class B1> struct conjunction<B1> : B1 { };
template<class B1, class... Bn>
struct conjunction<B1, Bn...>
: std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};


We end up inheriting from this specialization of std::conditional



template<class T, class F>
struct conditional<false, T, F> { typedef F type; };


This requires both class types for instantiation. Then how is the conjunction<Bn...> elided by the language?










share|improve this question













marked as duplicate by xskxzr, Baum mit Augen c++
Users with the  c++ badge can single-handedly close c++ questions as duplicates and reopen them as needed.

StackExchange.ready(function() {
if (StackExchange.options.isMobile) return;

$('.dupe-hammer-message-hover:not(.hover-bound)').each(function() {
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');

$hover.hover(
function() {
$hover.showInfoMessage('', {
messageElement: $msg.clone().show(),
transient: false,
position: { my: 'bottom left', at: 'top center', offsetTop: -7 },
dismissable: false,
relativeToBody: true
});
},
function() {
StackExchange.helpers.removeMessages();
}
);
});
});
Nov 18 '18 at 21:00


This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.











  • 1




    Now, add typedef HardError<int> zz; to your sample code. This will also compile without any errors. Now, try to instantiate zz, only then you get an error. Just because a class with a failed static assertion is referenced doesn't trigger the static assertion, but when you attempt to create an instance of the class. No instance of HardError gets instantiated during the unraveling of std::conjunction.
    – Sam Varshavchik
    Nov 18 '18 at 1:58












  • In addition, I think it is ill-formed, no diagnostic required, according to [temp.res]/8.1
    – xskxzr
    Nov 18 '18 at 16:55














4












4








4


1






This question already has an answer here:




  • static_assert and class templates

    2 answers




Given the following code (https://wandbox.org/permlink/Eof3RQs49weJMWan)



#include <tuple>
#include <type_traits>
#include <utility>

template <typename T>
inline constexpr auto always_false = false;

template <typename T>
class HardError {
static_assert(always_false<T>);
};

int main() {
std::ignore = std::conjunction<std::false_type, HardError<int>>{};
}


I am trying to understand why this does not error out with std::conjunction as used above. I understand that it is meant to allow short-circuiting so this does not happen, and that this is by design.



However, I don't understand the language rules that allow this to happen. Given the implementation of std::conjunction below



template<class...> struct conjunction : std::true_type { };
template<class B1> struct conjunction<B1> : B1 { };
template<class B1, class... Bn>
struct conjunction<B1, Bn...>
: std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};


We end up inheriting from this specialization of std::conditional



template<class T, class F>
struct conditional<false, T, F> { typedef F type; };


This requires both class types for instantiation. Then how is the conjunction<Bn...> elided by the language?










share|improve this question














This question already has an answer here:




  • static_assert and class templates

    2 answers




Given the following code (https://wandbox.org/permlink/Eof3RQs49weJMWan)



#include <tuple>
#include <type_traits>
#include <utility>

template <typename T>
inline constexpr auto always_false = false;

template <typename T>
class HardError {
static_assert(always_false<T>);
};

int main() {
std::ignore = std::conjunction<std::false_type, HardError<int>>{};
}


I am trying to understand why this does not error out with std::conjunction as used above. I understand that it is meant to allow short-circuiting so this does not happen, and that this is by design.



However, I don't understand the language rules that allow this to happen. Given the implementation of std::conjunction below



template<class...> struct conjunction : std::true_type { };
template<class B1> struct conjunction<B1> : B1 { };
template<class B1, class... Bn>
struct conjunction<B1, Bn...>
: std::conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};


We end up inheriting from this specialization of std::conditional



template<class T, class F>
struct conditional<false, T, F> { typedef F type; };


This requires both class types for instantiation. Then how is the conjunction<Bn...> elided by the language?





This question already has an answer here:




  • static_assert and class templates

    2 answers








c++ templates language-lawyer metaprogramming






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 18 '18 at 1:39









Curious

11.9k22471




11.9k22471




marked as duplicate by xskxzr, Baum mit Augen c++
Users with the  c++ badge can single-handedly close c++ questions as duplicates and reopen them as needed.

StackExchange.ready(function() {
if (StackExchange.options.isMobile) return;

$('.dupe-hammer-message-hover:not(.hover-bound)').each(function() {
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');

$hover.hover(
function() {
$hover.showInfoMessage('', {
messageElement: $msg.clone().show(),
transient: false,
position: { my: 'bottom left', at: 'top center', offsetTop: -7 },
dismissable: false,
relativeToBody: true
});
},
function() {
StackExchange.helpers.removeMessages();
}
);
});
});
Nov 18 '18 at 21:00


This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.






marked as duplicate by xskxzr, Baum mit Augen c++
Users with the  c++ badge can single-handedly close c++ questions as duplicates and reopen them as needed.

StackExchange.ready(function() {
if (StackExchange.options.isMobile) return;

$('.dupe-hammer-message-hover:not(.hover-bound)').each(function() {
var $hover = $(this).addClass('hover-bound'),
$msg = $hover.siblings('.dupe-hammer-message');

$hover.hover(
function() {
$hover.showInfoMessage('', {
messageElement: $msg.clone().show(),
transient: false,
position: { my: 'bottom left', at: 'top center', offsetTop: -7 },
dismissable: false,
relativeToBody: true
});
},
function() {
StackExchange.helpers.removeMessages();
}
);
});
});
Nov 18 '18 at 21:00


This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.










  • 1




    Now, add typedef HardError<int> zz; to your sample code. This will also compile without any errors. Now, try to instantiate zz, only then you get an error. Just because a class with a failed static assertion is referenced doesn't trigger the static assertion, but when you attempt to create an instance of the class. No instance of HardError gets instantiated during the unraveling of std::conjunction.
    – Sam Varshavchik
    Nov 18 '18 at 1:58












  • In addition, I think it is ill-formed, no diagnostic required, according to [temp.res]/8.1
    – xskxzr
    Nov 18 '18 at 16:55














  • 1




    Now, add typedef HardError<int> zz; to your sample code. This will also compile without any errors. Now, try to instantiate zz, only then you get an error. Just because a class with a failed static assertion is referenced doesn't trigger the static assertion, but when you attempt to create an instance of the class. No instance of HardError gets instantiated during the unraveling of std::conjunction.
    – Sam Varshavchik
    Nov 18 '18 at 1:58












  • In addition, I think it is ill-formed, no diagnostic required, according to [temp.res]/8.1
    – xskxzr
    Nov 18 '18 at 16:55








1




1




Now, add typedef HardError<int> zz; to your sample code. This will also compile without any errors. Now, try to instantiate zz, only then you get an error. Just because a class with a failed static assertion is referenced doesn't trigger the static assertion, but when you attempt to create an instance of the class. No instance of HardError gets instantiated during the unraveling of std::conjunction.
– Sam Varshavchik
Nov 18 '18 at 1:58






Now, add typedef HardError<int> zz; to your sample code. This will also compile without any errors. Now, try to instantiate zz, only then you get an error. Just because a class with a failed static assertion is referenced doesn't trigger the static assertion, but when you attempt to create an instance of the class. No instance of HardError gets instantiated during the unraveling of std::conjunction.
– Sam Varshavchik
Nov 18 '18 at 1:58














In addition, I think it is ill-formed, no diagnostic required, according to [temp.res]/8.1
– xskxzr
Nov 18 '18 at 16:55




In addition, I think it is ill-formed, no diagnostic required, according to [temp.res]/8.1
– xskxzr
Nov 18 '18 at 16:55












2 Answers
2






active

oldest

votes


















4














cppreference (the page you pulled that implementation from) provides an explanation for how it works:




Conjunction is short-circuiting: if there is a template type argument Bi with bool(Bi::value) == false, then instantiating conjunction<B1, ..., BN>::value does not require the instantiation of Bj::value for j > i.




Concretely, we can see this in action. Your line in main:



std::ignore = std::conjunction<std::false_type, HardError<int>>{};


is equivalent to:



std::ignore.operator=(conjunction<std::integral_constant<bool, 0>, HardError<int> >{{}});


Which should cause an instantiation, something like the following:



template<>
struct conjunction<std::integral_constant<bool, 0>, HardError<int> > : public std::integral_constant<bool, 0>
{
inline ~conjunction() noexcept = default;

};





share|improve this answer





















  • Welcome to StackOverflow :) I understand that something like this instantiation would be created by the compiler. I am trying to understand what language rules lead to this not being ill formed.
    – Curious
    Nov 18 '18 at 1:56





















2














The instantiation of a template does not trigger the instantiation of its template arguments. See [temp.inst/2]



This is a typical way to delay hard error.



template<class T> struct delay {
using run = T;
};

int main() {
// force instantiation of delay<HardError<int>>,
// but HardError<int> itself is not instantiated
sizeof(delay<HardError<int>>);

delay<HardError<int>> a; // OK, same as above

// Now HardError<int> is instantiated, static_assert failure
// sizeof(delay<HardError<int>>::run);
}


For the same reason, the instantiation of std::conditional<false, int, HardError<int>> won't cause the instantiation of HardError<int>



Furthermore, the template argument doesn't even need to be complete.



The following code is also valid:



struct incomplete_tag;

int main() { sizeof(delay<incomplete_tag>); }





share|improve this answer






























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    4














    cppreference (the page you pulled that implementation from) provides an explanation for how it works:




    Conjunction is short-circuiting: if there is a template type argument Bi with bool(Bi::value) == false, then instantiating conjunction<B1, ..., BN>::value does not require the instantiation of Bj::value for j > i.




    Concretely, we can see this in action. Your line in main:



    std::ignore = std::conjunction<std::false_type, HardError<int>>{};


    is equivalent to:



    std::ignore.operator=(conjunction<std::integral_constant<bool, 0>, HardError<int> >{{}});


    Which should cause an instantiation, something like the following:



    template<>
    struct conjunction<std::integral_constant<bool, 0>, HardError<int> > : public std::integral_constant<bool, 0>
    {
    inline ~conjunction() noexcept = default;

    };





    share|improve this answer





















    • Welcome to StackOverflow :) I understand that something like this instantiation would be created by the compiler. I am trying to understand what language rules lead to this not being ill formed.
      – Curious
      Nov 18 '18 at 1:56


















    4














    cppreference (the page you pulled that implementation from) provides an explanation for how it works:




    Conjunction is short-circuiting: if there is a template type argument Bi with bool(Bi::value) == false, then instantiating conjunction<B1, ..., BN>::value does not require the instantiation of Bj::value for j > i.




    Concretely, we can see this in action. Your line in main:



    std::ignore = std::conjunction<std::false_type, HardError<int>>{};


    is equivalent to:



    std::ignore.operator=(conjunction<std::integral_constant<bool, 0>, HardError<int> >{{}});


    Which should cause an instantiation, something like the following:



    template<>
    struct conjunction<std::integral_constant<bool, 0>, HardError<int> > : public std::integral_constant<bool, 0>
    {
    inline ~conjunction() noexcept = default;

    };





    share|improve this answer





















    • Welcome to StackOverflow :) I understand that something like this instantiation would be created by the compiler. I am trying to understand what language rules lead to this not being ill formed.
      – Curious
      Nov 18 '18 at 1:56
















    4












    4








    4






    cppreference (the page you pulled that implementation from) provides an explanation for how it works:




    Conjunction is short-circuiting: if there is a template type argument Bi with bool(Bi::value) == false, then instantiating conjunction<B1, ..., BN>::value does not require the instantiation of Bj::value for j > i.




    Concretely, we can see this in action. Your line in main:



    std::ignore = std::conjunction<std::false_type, HardError<int>>{};


    is equivalent to:



    std::ignore.operator=(conjunction<std::integral_constant<bool, 0>, HardError<int> >{{}});


    Which should cause an instantiation, something like the following:



    template<>
    struct conjunction<std::integral_constant<bool, 0>, HardError<int> > : public std::integral_constant<bool, 0>
    {
    inline ~conjunction() noexcept = default;

    };





    share|improve this answer












    cppreference (the page you pulled that implementation from) provides an explanation for how it works:




    Conjunction is short-circuiting: if there is a template type argument Bi with bool(Bi::value) == false, then instantiating conjunction<B1, ..., BN>::value does not require the instantiation of Bj::value for j > i.




    Concretely, we can see this in action. Your line in main:



    std::ignore = std::conjunction<std::false_type, HardError<int>>{};


    is equivalent to:



    std::ignore.operator=(conjunction<std::integral_constant<bool, 0>, HardError<int> >{{}});


    Which should cause an instantiation, something like the following:



    template<>
    struct conjunction<std::integral_constant<bool, 0>, HardError<int> > : public std::integral_constant<bool, 0>
    {
    inline ~conjunction() noexcept = default;

    };






    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Nov 18 '18 at 1:49









    user10668999

    411




    411












    • Welcome to StackOverflow :) I understand that something like this instantiation would be created by the compiler. I am trying to understand what language rules lead to this not being ill formed.
      – Curious
      Nov 18 '18 at 1:56




















    • Welcome to StackOverflow :) I understand that something like this instantiation would be created by the compiler. I am trying to understand what language rules lead to this not being ill formed.
      – Curious
      Nov 18 '18 at 1:56


















    Welcome to StackOverflow :) I understand that something like this instantiation would be created by the compiler. I am trying to understand what language rules lead to this not being ill formed.
    – Curious
    Nov 18 '18 at 1:56






    Welcome to StackOverflow :) I understand that something like this instantiation would be created by the compiler. I am trying to understand what language rules lead to this not being ill formed.
    – Curious
    Nov 18 '18 at 1:56















    2














    The instantiation of a template does not trigger the instantiation of its template arguments. See [temp.inst/2]



    This is a typical way to delay hard error.



    template<class T> struct delay {
    using run = T;
    };

    int main() {
    // force instantiation of delay<HardError<int>>,
    // but HardError<int> itself is not instantiated
    sizeof(delay<HardError<int>>);

    delay<HardError<int>> a; // OK, same as above

    // Now HardError<int> is instantiated, static_assert failure
    // sizeof(delay<HardError<int>>::run);
    }


    For the same reason, the instantiation of std::conditional<false, int, HardError<int>> won't cause the instantiation of HardError<int>



    Furthermore, the template argument doesn't even need to be complete.



    The following code is also valid:



    struct incomplete_tag;

    int main() { sizeof(delay<incomplete_tag>); }





    share|improve this answer




























      2














      The instantiation of a template does not trigger the instantiation of its template arguments. See [temp.inst/2]



      This is a typical way to delay hard error.



      template<class T> struct delay {
      using run = T;
      };

      int main() {
      // force instantiation of delay<HardError<int>>,
      // but HardError<int> itself is not instantiated
      sizeof(delay<HardError<int>>);

      delay<HardError<int>> a; // OK, same as above

      // Now HardError<int> is instantiated, static_assert failure
      // sizeof(delay<HardError<int>>::run);
      }


      For the same reason, the instantiation of std::conditional<false, int, HardError<int>> won't cause the instantiation of HardError<int>



      Furthermore, the template argument doesn't even need to be complete.



      The following code is also valid:



      struct incomplete_tag;

      int main() { sizeof(delay<incomplete_tag>); }





      share|improve this answer


























        2












        2








        2






        The instantiation of a template does not trigger the instantiation of its template arguments. See [temp.inst/2]



        This is a typical way to delay hard error.



        template<class T> struct delay {
        using run = T;
        };

        int main() {
        // force instantiation of delay<HardError<int>>,
        // but HardError<int> itself is not instantiated
        sizeof(delay<HardError<int>>);

        delay<HardError<int>> a; // OK, same as above

        // Now HardError<int> is instantiated, static_assert failure
        // sizeof(delay<HardError<int>>::run);
        }


        For the same reason, the instantiation of std::conditional<false, int, HardError<int>> won't cause the instantiation of HardError<int>



        Furthermore, the template argument doesn't even need to be complete.



        The following code is also valid:



        struct incomplete_tag;

        int main() { sizeof(delay<incomplete_tag>); }





        share|improve this answer














        The instantiation of a template does not trigger the instantiation of its template arguments. See [temp.inst/2]



        This is a typical way to delay hard error.



        template<class T> struct delay {
        using run = T;
        };

        int main() {
        // force instantiation of delay<HardError<int>>,
        // but HardError<int> itself is not instantiated
        sizeof(delay<HardError<int>>);

        delay<HardError<int>> a; // OK, same as above

        // Now HardError<int> is instantiated, static_assert failure
        // sizeof(delay<HardError<int>>::run);
        }


        For the same reason, the instantiation of std::conditional<false, int, HardError<int>> won't cause the instantiation of HardError<int>



        Furthermore, the template argument doesn't even need to be complete.



        The following code is also valid:



        struct incomplete_tag;

        int main() { sizeof(delay<incomplete_tag>); }






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 18 '18 at 2:45

























        answered Nov 18 '18 at 2:39









        liliscent

        13.6k41742




        13.6k41742















            Popular posts from this blog

            Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

            ComboBox Display Member on multiple fields

            Is it possible to collect Nectar points via Trainline?