MSVC constexpr function 'xyz' cannot result in a constant expression












10















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?










share|improve this question

























  • 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
















10















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?










share|improve this question

























  • 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














10












10








10


1






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?










share|improve this question
















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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



















  • 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












1 Answer
1






active

oldest

votes


















15














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





share|improve this answer
























  • 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











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%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









15














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





share|improve this answer
























  • 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
















15














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





share|improve this answer
























  • 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














15












15








15







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





share|improve this answer













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






share|improve this answer












share|improve this answer



share|improve this answer










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



















  • 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


















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%2f53888313%2fmsvc-constexpr-function-xyz-cannot-result-in-a-constant-expression%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

Plaza Victoria

Puebla de Zaragoza

Musa