How does short-circuiting work in std::conjunction [duplicate]
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?
c++ templates language-lawyer metaprogramming
marked as duplicate by xskxzr, Baum mit Augen
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.
add a comment |
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?
c++ templates language-lawyer metaprogramming
marked as duplicate by xskxzr, Baum mit Augen
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, addtypedef HardError<int> zz;
to your sample code. This will also compile without any errors. Now, try to instantiatezz
, 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 ofHardError
gets instantiated during the unraveling ofstd::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
add a comment |
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?
c++ templates language-lawyer metaprogramming
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
c++ templates language-lawyer metaprogramming
asked Nov 18 '18 at 1:39
Curious
11.9k22471
11.9k22471
marked as duplicate by xskxzr, Baum mit Augen
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
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, addtypedef HardError<int> zz;
to your sample code. This will also compile without any errors. Now, try to instantiatezz
, 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 ofHardError
gets instantiated during the unraveling ofstd::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
add a comment |
1
Now, addtypedef HardError<int> zz;
to your sample code. This will also compile without any errors. Now, try to instantiatezz
, 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 ofHardError
gets instantiated during the unraveling ofstd::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
add a comment |
2 Answers
2
active
oldest
votes
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
withbool(Bi::value) == false
, then instantiatingconjunction<B1, ..., BN>::value
does not require the instantiation ofBj::value
forj > 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;
};
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
add a comment |
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>); }
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
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
withbool(Bi::value) == false
, then instantiatingconjunction<B1, ..., BN>::value
does not require the instantiation ofBj::value
forj > 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;
};
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
add a comment |
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
withbool(Bi::value) == false
, then instantiatingconjunction<B1, ..., BN>::value
does not require the instantiation ofBj::value
forj > 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;
};
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
add a comment |
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
withbool(Bi::value) == false
, then instantiatingconjunction<B1, ..., BN>::value
does not require the instantiation ofBj::value
forj > 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;
};
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
withbool(Bi::value) == false
, then instantiatingconjunction<B1, ..., BN>::value
does not require the instantiation ofBj::value
forj > 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;
};
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
add a comment |
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
add a comment |
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>); }
add a comment |
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>); }
add a comment |
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>); }
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>); }
edited Nov 18 '18 at 2:45
answered Nov 18 '18 at 2:39
liliscent
13.6k41742
13.6k41742
add a comment |
add a comment |
1
Now, add
typedef HardError<int> zz;
to your sample code. This will also compile without any errors. Now, try to instantiatezz
, 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 ofHardError
gets instantiated during the unraveling ofstd::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