MSVC constexpr function 'xyz' cannot result in a constant expression
I've made a function that concatenates multiple smaller values into one larger value while preserving the bianry representation of the values (ex. to build an int argb
from multiple unsigned char r, g, b, a
). I know I can also achive this by bit shifting the values but that's not the matter of this question.
However, if I use the function to actually generate an integer from those values, msvc throws a compiler error:
error C3615: constexpr function 'Color::operator int' cannot result in a constant expression
note: failure was caused by call of undefined function or one not declared 'constexpr'
note: see usage of '<lambda_dcb9c20fcc2050e56c066522a838749d>::operator ()'
Here is a complete sample. Clang and gcc compile the code but msvc refuses:
#include <type_traits>
#include <memory>
namespace detail
{
template <typename From, typename To, size_t Size>
union binary_fusion_helper
{
const From from[Size];
const To to;
};
template <typename To, typename Arg, typename ...Args, typename = std::enable_if_t<(... && std::is_same_v<std::remove_reference_t<Arg>, std::remove_reference_t<Args>>)>>
constexpr To binary_fusion(Arg arg, Args... args)
{
using in_t = std::remove_reference_t<Arg>;
using out_t = To;
static_assert(sizeof(out_t) == sizeof(in_t) * (sizeof...(Args) + 1), "The target type must be of exact same size as the sum of all argument types.");
constexpr size_t num = sizeof(out_t) / sizeof(in_t);
return binary_fusion_helper<in_t, out_t, num> { std::forward<Arg>(arg), std::forward<Args>(args)... }.to;
}
}
template <typename To>
constexpr auto binary_fusion = (auto ...values) -> To
{
return detail::binary_fusion<std::remove_reference_t<To>>(values...);
};
struct Color
{
float r, g, b, a;
explicit constexpr operator int() const noexcept
{
return binary_fusion<int>(static_cast<unsigned char>(r * 255), static_cast<unsigned char>(g * 255),
static_cast<unsigned char>(b * 255), static_cast<unsigned char>(a * 255));
}
};
Do clang and gcc just ignore that the code will never run as a constexpr or is msvc wrong? And if msvc is correct, why can't the function run at compile time?
c++ visual-c++ c++17
add a comment |
I've made a function that concatenates multiple smaller values into one larger value while preserving the bianry representation of the values (ex. to build an int argb
from multiple unsigned char r, g, b, a
). I know I can also achive this by bit shifting the values but that's not the matter of this question.
However, if I use the function to actually generate an integer from those values, msvc throws a compiler error:
error C3615: constexpr function 'Color::operator int' cannot result in a constant expression
note: failure was caused by call of undefined function or one not declared 'constexpr'
note: see usage of '<lambda_dcb9c20fcc2050e56c066522a838749d>::operator ()'
Here is a complete sample. Clang and gcc compile the code but msvc refuses:
#include <type_traits>
#include <memory>
namespace detail
{
template <typename From, typename To, size_t Size>
union binary_fusion_helper
{
const From from[Size];
const To to;
};
template <typename To, typename Arg, typename ...Args, typename = std::enable_if_t<(... && std::is_same_v<std::remove_reference_t<Arg>, std::remove_reference_t<Args>>)>>
constexpr To binary_fusion(Arg arg, Args... args)
{
using in_t = std::remove_reference_t<Arg>;
using out_t = To;
static_assert(sizeof(out_t) == sizeof(in_t) * (sizeof...(Args) + 1), "The target type must be of exact same size as the sum of all argument types.");
constexpr size_t num = sizeof(out_t) / sizeof(in_t);
return binary_fusion_helper<in_t, out_t, num> { std::forward<Arg>(arg), std::forward<Args>(args)... }.to;
}
}
template <typename To>
constexpr auto binary_fusion = (auto ...values) -> To
{
return detail::binary_fusion<std::remove_reference_t<To>>(values...);
};
struct Color
{
float r, g, b, a;
explicit constexpr operator int() const noexcept
{
return binary_fusion<int>(static_cast<unsigned char>(r * 255), static_cast<unsigned char>(g * 255),
static_cast<unsigned char>(b * 255), static_cast<unsigned char>(a * 255));
}
};
Do clang and gcc just ignore that the code will never run as a constexpr or is msvc wrong? And if msvc is correct, why can't the function run at compile time?
c++ visual-c++ c++17
Which version of MSVC are you using?
– Hiroki
Dec 21 '18 at 16:55
@Hiroki the very latest. Not sure which one it is, can't access my computer atm.
– Timo
Dec 21 '18 at 16:57
You have to use shifting instead of union.
– Robert Andrzejuk
Dec 21 '18 at 17:22
add a comment |
I've made a function that concatenates multiple smaller values into one larger value while preserving the bianry representation of the values (ex. to build an int argb
from multiple unsigned char r, g, b, a
). I know I can also achive this by bit shifting the values but that's not the matter of this question.
However, if I use the function to actually generate an integer from those values, msvc throws a compiler error:
error C3615: constexpr function 'Color::operator int' cannot result in a constant expression
note: failure was caused by call of undefined function or one not declared 'constexpr'
note: see usage of '<lambda_dcb9c20fcc2050e56c066522a838749d>::operator ()'
Here is a complete sample. Clang and gcc compile the code but msvc refuses:
#include <type_traits>
#include <memory>
namespace detail
{
template <typename From, typename To, size_t Size>
union binary_fusion_helper
{
const From from[Size];
const To to;
};
template <typename To, typename Arg, typename ...Args, typename = std::enable_if_t<(... && std::is_same_v<std::remove_reference_t<Arg>, std::remove_reference_t<Args>>)>>
constexpr To binary_fusion(Arg arg, Args... args)
{
using in_t = std::remove_reference_t<Arg>;
using out_t = To;
static_assert(sizeof(out_t) == sizeof(in_t) * (sizeof...(Args) + 1), "The target type must be of exact same size as the sum of all argument types.");
constexpr size_t num = sizeof(out_t) / sizeof(in_t);
return binary_fusion_helper<in_t, out_t, num> { std::forward<Arg>(arg), std::forward<Args>(args)... }.to;
}
}
template <typename To>
constexpr auto binary_fusion = (auto ...values) -> To
{
return detail::binary_fusion<std::remove_reference_t<To>>(values...);
};
struct Color
{
float r, g, b, a;
explicit constexpr operator int() const noexcept
{
return binary_fusion<int>(static_cast<unsigned char>(r * 255), static_cast<unsigned char>(g * 255),
static_cast<unsigned char>(b * 255), static_cast<unsigned char>(a * 255));
}
};
Do clang and gcc just ignore that the code will never run as a constexpr or is msvc wrong? And if msvc is correct, why can't the function run at compile time?
c++ visual-c++ c++17
I've made a function that concatenates multiple smaller values into one larger value while preserving the bianry representation of the values (ex. to build an int argb
from multiple unsigned char r, g, b, a
). I know I can also achive this by bit shifting the values but that's not the matter of this question.
However, if I use the function to actually generate an integer from those values, msvc throws a compiler error:
error C3615: constexpr function 'Color::operator int' cannot result in a constant expression
note: failure was caused by call of undefined function or one not declared 'constexpr'
note: see usage of '<lambda_dcb9c20fcc2050e56c066522a838749d>::operator ()'
Here is a complete sample. Clang and gcc compile the code but msvc refuses:
#include <type_traits>
#include <memory>
namespace detail
{
template <typename From, typename To, size_t Size>
union binary_fusion_helper
{
const From from[Size];
const To to;
};
template <typename To, typename Arg, typename ...Args, typename = std::enable_if_t<(... && std::is_same_v<std::remove_reference_t<Arg>, std::remove_reference_t<Args>>)>>
constexpr To binary_fusion(Arg arg, Args... args)
{
using in_t = std::remove_reference_t<Arg>;
using out_t = To;
static_assert(sizeof(out_t) == sizeof(in_t) * (sizeof...(Args) + 1), "The target type must be of exact same size as the sum of all argument types.");
constexpr size_t num = sizeof(out_t) / sizeof(in_t);
return binary_fusion_helper<in_t, out_t, num> { std::forward<Arg>(arg), std::forward<Args>(args)... }.to;
}
}
template <typename To>
constexpr auto binary_fusion = (auto ...values) -> To
{
return detail::binary_fusion<std::remove_reference_t<To>>(values...);
};
struct Color
{
float r, g, b, a;
explicit constexpr operator int() const noexcept
{
return binary_fusion<int>(static_cast<unsigned char>(r * 255), static_cast<unsigned char>(g * 255),
static_cast<unsigned char>(b * 255), static_cast<unsigned char>(a * 255));
}
};
Do clang and gcc just ignore that the code will never run as a constexpr or is msvc wrong? And if msvc is correct, why can't the function run at compile time?
c++ visual-c++ c++17
c++ visual-c++ c++17
edited Dec 21 '18 at 17:01
Justin
13.3k95396
13.3k95396
asked Dec 21 '18 at 16:52
TimoTimo
1,4661624
1,4661624
Which version of MSVC are you using?
– Hiroki
Dec 21 '18 at 16:55
@Hiroki the very latest. Not sure which one it is, can't access my computer atm.
– Timo
Dec 21 '18 at 16:57
You have to use shifting instead of union.
– Robert Andrzejuk
Dec 21 '18 at 17:22
add a comment |
Which version of MSVC are you using?
– Hiroki
Dec 21 '18 at 16:55
@Hiroki the very latest. Not sure which one it is, can't access my computer atm.
– Timo
Dec 21 '18 at 16:57
You have to use shifting instead of union.
– Robert Andrzejuk
Dec 21 '18 at 17:22
Which version of MSVC are you using?
– Hiroki
Dec 21 '18 at 16:55
Which version of MSVC are you using?
– Hiroki
Dec 21 '18 at 16:55
@Hiroki the very latest. Not sure which one it is, can't access my computer atm.
– Timo
Dec 21 '18 at 16:57
@Hiroki the very latest. Not sure which one it is, can't access my computer atm.
– Timo
Dec 21 '18 at 16:57
You have to use shifting instead of union.
– Robert Andrzejuk
Dec 21 '18 at 17:22
You have to use shifting instead of union.
– Robert Andrzejuk
Dec 21 '18 at 17:22
add a comment |
1 Answer
1
active
oldest
votes
Every compiler is correct. The rule in [dcl.constexpr]/5 is:
For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression, or, for a constructor, a constant initializer for some object ([basic.start.static]), the program is ill-formed, no diagnostic required.
There is no set of arguments you can pass in to binary_fusion
that would allow it to be evaluated as a core constant expression, so declaring it constexpr
is ill-formed, NDR. The reason this is the case is because detail::binary_fusion()
initializes a union with one active member and then reads from the inactive member, which you are not allowed to do in constant expressions ([expr.const]/4.8):
an lvalue-to-rvalue conversion that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;
MSVC somehow diagnoses this, gcc/clang happen not to. All compilers correctly diagnose this:
constexpr Color c{1.0f, 1.0f, 1.0f, 1.0f};
constexpr int i = static_cast<int>(c); // error: not a constant expression
Wow! "lvalue-to-rvalue conversion that is applied to a glvalue... " that... is not something mere mortals would get is happening here.
– Robert Andrzejuk
Dec 21 '18 at 18:01
2
@RobertAndrzejuk It translates roughly to "actually reading it".
– Yakk - Adam Nevraumont
Dec 21 '18 at 18:34
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53888313%2fmsvc-constexpr-function-xyz-cannot-result-in-a-constant-expression%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
Every compiler is correct. The rule in [dcl.constexpr]/5 is:
For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression, or, for a constructor, a constant initializer for some object ([basic.start.static]), the program is ill-formed, no diagnostic required.
There is no set of arguments you can pass in to binary_fusion
that would allow it to be evaluated as a core constant expression, so declaring it constexpr
is ill-formed, NDR. The reason this is the case is because detail::binary_fusion()
initializes a union with one active member and then reads from the inactive member, which you are not allowed to do in constant expressions ([expr.const]/4.8):
an lvalue-to-rvalue conversion that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;
MSVC somehow diagnoses this, gcc/clang happen not to. All compilers correctly diagnose this:
constexpr Color c{1.0f, 1.0f, 1.0f, 1.0f};
constexpr int i = static_cast<int>(c); // error: not a constant expression
Wow! "lvalue-to-rvalue conversion that is applied to a glvalue... " that... is not something mere mortals would get is happening here.
– Robert Andrzejuk
Dec 21 '18 at 18:01
2
@RobertAndrzejuk It translates roughly to "actually reading it".
– Yakk - Adam Nevraumont
Dec 21 '18 at 18:34
add a comment |
Every compiler is correct. The rule in [dcl.constexpr]/5 is:
For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression, or, for a constructor, a constant initializer for some object ([basic.start.static]), the program is ill-formed, no diagnostic required.
There is no set of arguments you can pass in to binary_fusion
that would allow it to be evaluated as a core constant expression, so declaring it constexpr
is ill-formed, NDR. The reason this is the case is because detail::binary_fusion()
initializes a union with one active member and then reads from the inactive member, which you are not allowed to do in constant expressions ([expr.const]/4.8):
an lvalue-to-rvalue conversion that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;
MSVC somehow diagnoses this, gcc/clang happen not to. All compilers correctly diagnose this:
constexpr Color c{1.0f, 1.0f, 1.0f, 1.0f};
constexpr int i = static_cast<int>(c); // error: not a constant expression
Wow! "lvalue-to-rvalue conversion that is applied to a glvalue... " that... is not something mere mortals would get is happening here.
– Robert Andrzejuk
Dec 21 '18 at 18:01
2
@RobertAndrzejuk It translates roughly to "actually reading it".
– Yakk - Adam Nevraumont
Dec 21 '18 at 18:34
add a comment |
Every compiler is correct. The rule in [dcl.constexpr]/5 is:
For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression, or, for a constructor, a constant initializer for some object ([basic.start.static]), the program is ill-formed, no diagnostic required.
There is no set of arguments you can pass in to binary_fusion
that would allow it to be evaluated as a core constant expression, so declaring it constexpr
is ill-formed, NDR. The reason this is the case is because detail::binary_fusion()
initializes a union with one active member and then reads from the inactive member, which you are not allowed to do in constant expressions ([expr.const]/4.8):
an lvalue-to-rvalue conversion that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;
MSVC somehow diagnoses this, gcc/clang happen not to. All compilers correctly diagnose this:
constexpr Color c{1.0f, 1.0f, 1.0f, 1.0f};
constexpr int i = static_cast<int>(c); // error: not a constant expression
Every compiler is correct. The rule in [dcl.constexpr]/5 is:
For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression, or, for a constructor, a constant initializer for some object ([basic.start.static]), the program is ill-formed, no diagnostic required.
There is no set of arguments you can pass in to binary_fusion
that would allow it to be evaluated as a core constant expression, so declaring it constexpr
is ill-formed, NDR. The reason this is the case is because detail::binary_fusion()
initializes a union with one active member and then reads from the inactive member, which you are not allowed to do in constant expressions ([expr.const]/4.8):
an lvalue-to-rvalue conversion that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;
MSVC somehow diagnoses this, gcc/clang happen not to. All compilers correctly diagnose this:
constexpr Color c{1.0f, 1.0f, 1.0f, 1.0f};
constexpr int i = static_cast<int>(c); // error: not a constant expression
answered Dec 21 '18 at 17:03
BarryBarry
179k19310569
179k19310569
Wow! "lvalue-to-rvalue conversion that is applied to a glvalue... " that... is not something mere mortals would get is happening here.
– Robert Andrzejuk
Dec 21 '18 at 18:01
2
@RobertAndrzejuk It translates roughly to "actually reading it".
– Yakk - Adam Nevraumont
Dec 21 '18 at 18:34
add a comment |
Wow! "lvalue-to-rvalue conversion that is applied to a glvalue... " that... is not something mere mortals would get is happening here.
– Robert Andrzejuk
Dec 21 '18 at 18:01
2
@RobertAndrzejuk It translates roughly to "actually reading it".
– Yakk - Adam Nevraumont
Dec 21 '18 at 18:34
Wow! "lvalue-to-rvalue conversion that is applied to a glvalue... " that... is not something mere mortals would get is happening here.
– Robert Andrzejuk
Dec 21 '18 at 18:01
Wow! "lvalue-to-rvalue conversion that is applied to a glvalue... " that... is not something mere mortals would get is happening here.
– Robert Andrzejuk
Dec 21 '18 at 18:01
2
2
@RobertAndrzejuk It translates roughly to "actually reading it".
– Yakk - Adam Nevraumont
Dec 21 '18 at 18:34
@RobertAndrzejuk It translates roughly to "actually reading it".
– Yakk - Adam Nevraumont
Dec 21 '18 at 18:34
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53888313%2fmsvc-constexpr-function-xyz-cannot-result-in-a-constant-expression%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
Which version of MSVC are you using?
– Hiroki
Dec 21 '18 at 16:55
@Hiroki the very latest. Not sure which one it is, can't access my computer atm.
– Timo
Dec 21 '18 at 16:57
You have to use shifting instead of union.
– Robert Andrzejuk
Dec 21 '18 at 17:22