Request for interest: Constexpr function parameters

2,076 views
Skip to first unread message

talz...@gmail.com

unread,
Apr 15, 2013, 12:43:21 PM4/15/13
to std-pr...@isocpp.org
Hello everyone! 
Lately I've been concerned with how we access tuple elements.
The syntax 'std::get<0>(t)' seems highly unintuitive to me.
What I would really like to be able to write is 't[0]'.
For this we need constexpr function parameters. So we can define operator[] as follows:
auto operator[] (constexpr int i) -> decltype(std::get<i>(*this)) {
    return std::get<i>(*this);
}
(or something similar)

I think this would help not only for tuples.
For example, it is not possible today to pass arbitrary constexpr objects to functions because you must pass them as a template parameter.
Template parameters must be an int/char/typename or the like. I think this really limits constexpr objects in terms of usability.

As I see it, we can only return and operate on constexpr objects, but we can't pass them around and maintain their "constexprness".
For example here's a silly example:
void foo(constexpr int i) {
    std::array<int, i> arr;
}

What do you think?
Message has been deleted

Nicol Bolas

unread,
Apr 15, 2013, 8:28:21 PM4/15/13
to std-pr...@isocpp.org, talz...@gmail.com
On Monday, April 15, 2013 9:43:21 AM UTC-7, talz...@gmail.com wrote:
Hello everyone! 
Lately I've been concerned with how we access tuple elements.
The syntax 'std::get<0>(t)' seems highly unintuitive to me.
What I would really like to be able to write is 't[0]'.
For this we need constexpr function parameters. So we can define operator[] as follows:
auto operator[] (constexpr int i) -> decltype(std::get<i>(*this)) {
    return std::get<i>(*this);
}
(or something similar)

I think this would help not only for tuples.
For example, it is not possible today to pass arbitrary constexpr objects to functions because you must pass them as a template parameter.

Nonsense. You can pass constexpr objects to constexpr functions just fine. There is no requirement that they will be evaluated at compile-time, but that's true of any constexpr expression that doesn't absolutely need to be compile-time evaluated.
 

talz...@gmail.com

unread,
Apr 15, 2013, 8:37:46 PM4/15/13
to std-pr...@isocpp.org, talz...@gmail.com
You are missing the point.
This has nothing to do with constexpr functions.
What I'm proposing is to be able to pass constexpr objects with no regard as to the function being constexpr.
In other words but I'm proposing is for the following syntax:

void foo(constexpr int i) {}

to be indentical to:

template <int i>
void foo() {}

The difference is that the constexpr version is easier to understand and that it allows you
to pass arbitrary constexpr object types instead of just some types.

Nicol Bolas

unread,
Apr 16, 2013, 12:41:54 AM4/16/13
to std-pr...@isocpp.org, talz...@gmail.com
On Monday, April 15, 2013 5:37:46 PM UTC-7, talz...@gmail.com wrote:
You are missing the point.
This has nothing to do with constexpr functions.
What I'm proposing is to be able to pass constexpr objects with no regard as to the function being constexpr.
In other words but I'm proposing is for the following syntax:

void foo(constexpr int i) {}

to be indentical to:

template <int i>
void foo() {}

The difference is that the constexpr version is easier to understand and that it allows you
to pass arbitrary constexpr object types instead of just some types.

There are reasons why templates don't allow arbitrary objects as template parameters. Among other things, template instantiation requires code generation. And code generation is contingent on what you instantiate a template with. `foo<5>` is a different function from `foo<4>`. If you instantiate a template with the same parameters in two places, you should get the same function generated. Allowing arbitrary objects as templates makes it very difficult to know what "the same parameters" even means.

In short, it's a bad idea. Just because you changed the syntax around it doesn't mean that it isn't still a bad idea. Template parameters are different things from function arguments, and they should not be treated the same way.

Furthermore, there's no reason why a function's arguments should ever be prevented from taking values that aren't compile-time constants. What would you use it for? Why would you want to prevent someone from calling `foo(integerValue)`?

Mikael Kilpeläinen

unread,
Apr 16, 2013, 5:46:19 AM4/16/13
to std-pr...@isocpp.org
Just to point out something related:

http://open-std.org/JTC1/SC22/WG21/docs/papers/2012/n3413.html
> be prevented from taking values that /aren't/ compile-time constants.
> What would you use it for? Why would you want to /prevent/ someone
> from calling `foo(integerValue)`?
>
> --
>
> ---
> You received this message because you are subscribed to the Google
> Groups "ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to std-proposal...@isocpp.org.
> To post to this group, send email to std-pr...@isocpp.org.
> Visit this group at
> http://groups.google.com/a/isocpp.org/group/std-proposals/?hl=en.
>
>

talz...@gmail.com

unread,
Apr 16, 2013, 6:21:36 AM4/16/13
to std-pr...@isocpp.org
Thank you! I didn't notice that proposal. I'm all for it =)
Maybe then we could stop having things like ratio<3,4> and be able to pass normal constexpr objects in template specifications.

Isaac Supeene

unread,
Apr 18, 2014, 12:57:51 AM4/18/14
to std-pr...@isocpp.org
I'd actually like to see this as a form of template argument deduction.
For example:

template <int i>
int operator [](constexpr int i) {
// Same i as function parameter and template parameter.
return i;
}

This is especially useful when you want to return a result of a
different type based on the value, for example when taking the exponent
of an MKS quantity:

template <int M, int K, int S>
class Quantity {
// Represents exponents
template <int i>
// Deduce template argument based on function argument.
Quantity<M * i, K * i, S * i> operator [](constexpr int i) {
return Quantity<M * i, K * i, S * i>(std::pow(this->value, i));
}
};

Matching names may not be the way to go, though... maybe some other syntax
that binds a template parameter to a function parameter, like
'constexpr int j -> i', or something like that. Then you can have both
beautiful and type-rich code for stuff like
'Force f = 12.5_kg*m/s[2]', where _kg is a user-defined literal suffix,
and m and s are constants.

Sean Middleditch

unread,
Apr 18, 2014, 1:22:43 PM4/18/14
to std-pr...@isocpp.org, talz...@gmail.com
On Monday, April 15, 2013 9:41:54 PM UTC-7, Nicol Bolas wrote:

Furthermore, there's no reason why a function's arguments should ever be prevented from taking values that aren't compile-time constants. What would you use it for? Why would you want to prevent someone from calling `foo(integerValue)`?

Assuming that overloading is part of the bargain:

There's cases where you might want to select algorithms or implementations based on whether a compiler is doing the work or a CPU is doing the work. You can't use CPU intrinsics in constexpr functions, for instance (especially in cross-compiles and the like), but a hand-optimized SSE version of some operation can be a significant runtime enhancement over the constexpr-friendly version (especially in debug builds; it's nice to have a playable game in debug-mode and not just a pretty slideshow). The same can go for hash functions. It could also be handy to overload based on string literals (right now you can't tell a string literal apart from any random character buffer) for certain kinds of string-based interfaces (if nothing else it lets you determine at compile time if you need a copy of a string or can just use its address), which constexpr arguments at least makes a bit easier if not fool-proof. You can now just name them separately and with some clear convention, but that needlessly complicates template logic and functional interface design.
Message has been deleted

Constructor

unread,
Apr 26, 2014, 5:08:20 AM4/26/14
to std-pr...@isocpp.org, talz...@gmail.com
Lately I've been concerned with how we access tuple elements.
The syntax 'std::get<0>(t)' seems highly unintuitive to me.
What I would really like to be able to write is 't[0]'.

It can be achieved using C++11 user-defined literals:

#include <iostream>
#include <tuple>
#include <type_traits>
#include <string>

template <size_t i>
using Index = std::integral_constant<size_t, i>;

template <class... Types>
struct Tuple : std::tuple<Types...>
{
    using std::tuple<Types...>::tuple;
    
    template <size_t i>
    auto operator [] (const Index<i>&) -> decltype(std::get<i>(*this))
    {
        return std::get<i>(*this);
    }
    
    template <size_t i>
    auto operator [] (const Index<i>&) const -> decltype(std::get<i>(*this))
    {
        return std::get<i>(*this);
    }
};

template <size_t N, char... Digits>
struct Number_ : std::integral_constant<size_t, N> {};

template <size_t N, char Digit, char... Digits>
struct Number_<N, Digit, Digits...> :
    std::integral_constant<size_t, Number_<N * 10 + (Digit - '0'), Digits...>::value>
{
    static_assert(('0' <= Digit) && (Digit <= '9'), "Invalid digit in index string");
};

template <char... Digits>
struct Number : Number_<0, Digits...> {};

template <char... Digits>
constexpr Index<Number<Digits...>::value> operator "" _i() noexcept
{
    return {};
}

int main() {
    int i = 42;
    double f = 3.14;
    std::string s = "Hello, world!";
    Tuple<int, double, std::string> t(i, f, s);
    
    std::cout << t[0_i] << std::endl;
    std::cout << t[1_i] << std::endl;
    std::cout << t[2_i] << std::endl;
}

See live example.

Another way is to use C++14 variable templates:

// ...

template <size_t i>
constexpr Index<i> I{};

// ...
   
std::cout << t[I<0>] << std::endl;
std::cout << t[I<1>] << std::endl;
std::cout << t[I<2>] << std::endl;

// ...

Sean Middleditch

unread,
Apr 26, 2014, 8:20:43 PM4/26/14
to std-pr...@isocpp.org, talz...@gmail.com
On Saturday, April 26, 2014 2:08:20 AM UTC-7, Constructor wrote:
Lately I've been concerned with how we access tuple elements.
The syntax 'std::get<0>(t)' seems highly unintuitive to me.
What I would really like to be able to write is 't[0]'.

It can be achieved using C++11 user-defined literals:

Some feedback I got on N3761 was that this exact feature was something they'd like to explore instead of a library solution. I've been meaning to write a follow-up paper but am unlikely to get to it anytime soon.

Róbert Dávid

unread,
May 1, 2014, 11:00:11 AM5/1/14
to std-pr...@isocpp.org, talz...@gmail.com
Actually, cleaner solution for the tuple is also possible:

#include <tuple>
#include <string>
template<typename... Types>
struct Purr {
    std
::tuple<Types...> t;
   
template<int i>
   
auto get() -> decltype(std::get<i>(t)) { return std::get<i>(t); }
};
int main() {
   
Purr<int, double, std::string> p;
    p
.get<0>() = 1;
    p
.get<1>() = 4.5;
    p
.get<2>() = "meow";
}

Works in MSVC2013, GCC 4.9.0 and Clang 3.3: http://goo.gl/MPHhMb
(Although, I can still imagine that it's somehow non-standard, but it's just formality after major compilers accept it :) )

A bit more for constexpr parameters: I love promoting every possible computation to compile time, after all, noone expects to generate (release) code that adds 2+2 at runtime for "int i = 2+2;". If the constexpr parameters could mean to allow for generating a runtime and a "compile-time" versions of the function, I'd be very interested. Example:

int foo(constexpr bool opt1, constexpr bool opt2) {
   
if (opt1) lengtyStuff();
    moreStuff();
    if (opt2
) stuffstuff();
    evenMoreStuff();   
}
int main() {
    bool userChoice1
= askUser();
    bool
userChoice2 = askUser();
   
foo(userChoice1, userChoice2); //generates code with 2 branch instructions
    foo
(true, false); //no branch instruction, aka "foo<true, false>"
    foo(userChoice1, true); //1 branch instr, aka "foo<true>(userChoice1)"
}
(In the middle of tight loops, this can matter quite a lot.)

Regards, Robert

Achille Roussel

unread,
Jun 9, 2014, 2:27:05 PM6/9/14
to std-pr...@isocpp.org, talz...@gmail.com
Hello,

Do you know if any progress has been made regarding your idea? I've ran into the exact same problem, I was thinking the tuple syntax for accessing elements was kind of complicated so I tried to see what the limitations were for having a subscript operator and came to the conclusion that C++ would need something like constexpr function parameters... which brought me to this post.


On Monday, April 15, 2013 9:43:21 AM UTC-7, talz...@gmail.com wrote:

talz...@gmail.com

unread,
Jun 10, 2014, 8:56:34 AM6/10/14
to std-pr...@isocpp.org, talz...@gmail.com
I'm not aware of any progress..
I still think it's a good idea

sasho648

unread,
Jan 27, 2015, 12:51:01 PM1/27/15
to std-pr...@isocpp.org, talz...@gmail.com
Me too. I was going to propose it but got too much work to do nowdays. You could also look my suggestion here.

sasho648

unread,
Jan 27, 2015, 12:56:08 PM1/27/15
to std-pr...@isocpp.org, talz...@gmail.com
But I suppose this is because people are rarely interested in the preprocessor and meta-programming.

Douglas Boffey

unread,
Jan 27, 2015, 1:38:08 PM1/27/15
to std-pr...@isocpp.org, talz...@gmail.com
I don't get it. What has this proposal to do with the preprocessor ?

On 1/27/15, sasho648 <sash...@mail.bg> wrote:
> But I suppose this is because people are rarely interested in the
> preprocessor and meta-programming.
>
> вторник, 27 януари 2015 г., 19:51:01 UTC+2, sasho648 написа:
>>
>> Me too. I was going to propose it but got too much work to do nowdays. You
>>
>> could also look my suggestion here
>> <https://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/Some$20constexpr/std-proposals/B5R4IsLgRgs/hjE78zL_qo8J>
>> .
> --
>
> ---
> You received this message because you are subscribed to the Google Groups
> "ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to std-proposal...@isocpp.org.
> To post to this group, send email to std-pr...@isocpp.org.
> Visit this group at
> http://groups.google.com/a/isocpp.org/group/std-proposals/.
>

Douglas Boffey

unread,
Jan 27, 2015, 1:44:07 PM1/27/15
to std-pr...@isocpp.org, talz...@gmail.com
As for metaprogramming, I believe there is a LOT of interest!

On 1/27/15, sasho648 <sash...@mail.bg> wrote:
> But I suppose this is because people are rarely interested in the
> preprocessor and meta-programming.
>
> вторник, 27 януари 2015 г., 19:51:01 UTC+2, sasho648 написа:
>>
>> Me too. I was going to propose it but got too much work to do nowdays. You
>>
>> could also look my suggestion here
>> <https://groups.google.com/a/isocpp.org/forum/#!searchin/std-proposals/Some$20constexpr/std-proposals/B5R4IsLgRgs/hjE78zL_qo8J>
>> .

dgutson .

unread,
Apr 6, 2016, 12:32:00 PM4/6/16
to std-proposals
Sorry for top-posting. I want to bring this issue again, please let's
make some effort to find a solution since it's important.

This would be very useful allowing lambdas in TMP, and also for static_asserts.
There is also a specific but yet real-life issue, which is intrinsics:
some intrinsics require arguments to be literals since those arguments
will become immediates in the associated instruction.
I'm trying to write an AES related program using Intel's AES-NI for
which gcc (and I think MSVC) have intrinsics, such as
_mm_aeskeygenassist_si128 ("the last argument must be an 8-bit
immediate").
There is no C++ way of doing this program with compile time loop
unrolling. If I would be able to do
Loop<0, 9>::operate([ ](constexpr unsigned int index) {
the_intrinsic(index); });
with the constexpr argument transformed into a non-type template
argument, then the problem would be solved.
I could also say static_assert(index < 10) and it would work too.

Would memoization be an issue here? Is that what Nicol Bolas refers
to? Let's start the discussion again.

Thanks,

Daniel.
--
Who’s got the sweetest disposition?
One guess, that’s who?
Who’d never, ever start an argument?
Who never shows a bit of temperament?
Who's never wrong but always right?
Who'd never dream of starting a fight?
Who get stuck with all the bad luck?

dgutson .

unread,
Apr 6, 2016, 12:52:07 PM4/6/16
to std-proposals
On Wed, Apr 6, 2016 at 1:31 PM, dgutson . <daniel...@gmail.com> wrote:
> Sorry for top-posting. I want to bring this issue again, please let's
> make some effort to find a solution since it's important.

Sorry, I think I actually found a workaround with generic lambdas:

template <class Function>
void fun(Function f)
{
f(std::integral_constant<int, 1>());
}

int main()
{
fun([](auto x){ std::cout << decltype(x)::value << std::endl; });
}

I will try my code using this idea; is this a solution for the OP?

Paul Fultz II

unread,
Apr 6, 2016, 12:54:44 PM4/6/16
to ISO C++ Standard - Future Proposals


On Wednesday, April 6, 2016 at 11:32:00 AM UTC-5, dgutson wrote:
Sorry for top-posting. I want to bring this issue again, please let's
make some effort to find a solution since it's important.

This would be very useful allowing lambdas in TMP, and also for static_asserts.
There is also a specific but yet real-life issue, which is intrinsics:
some intrinsics require arguments to be literals since those arguments
will become immediates in the associated instruction.
I'm trying to write an AES related program using Intel's AES-NI for
which gcc (and I think MSVC) have intrinsics, such as
_mm_aeskeygenassist_si128 ("the last argument must be an 8-bit
immediate").
There is no C++ way of doing this program with compile time loop
unrolling. If I would be able to do
     Loop<0, 9>::operate([ ](constexpr unsigned int index) {
the_intrinsic(index); });
with the constexpr argument transformed into a non-type template
argument, then the problem would be solved.
I could also say static_assert(index < 10) and it would work too.

Why wouldn't integral constants work? You can already write this:

template<class IntegralConstant>
void foo(IntegralConstant index)
{
    static_assert(index < 10);
}
foo(std::integral_constant<unsigned int, 9>{});

And using user-defined literals, the function could be called like this:

foo(9_c);

So your loop unrolling could be written like this:

Loop<0, 9>::operate([ ](auto index) { the_intrinsic(index); });

If for some reason, the compiler doesn't call the implicit conversions with the intrinsic(very unlikely), then it could be written like this as well:

Loop<0, 9>::operate([ ](auto I) {
    static constexpr unsigned int index = I;
    the_intrinsic(index);
});

 

dgutson .

unread,
Apr 6, 2016, 1:17:14 PM4/6/16
to std-proposals
On Wed, Apr 6, 2016 at 1:54 PM, Paul Fultz II <pful...@gmail.com> wrote:
>
>
> On Wednesday, April 6, 2016 at 11:32:00 AM UTC-5, dgutson wrote:
>>
>> Sorry for top-posting. I want to bring this issue again, please let's
>> make some effort to find a solution since it's important.
>>
>> This would be very useful allowing lambdas in TMP, and also for
>> static_asserts.
>> There is also a specific but yet real-life issue, which is intrinsics:
>> some intrinsics require arguments to be literals since those arguments
>> will become immediates in the associated instruction.
>> I'm trying to write an AES related program using Intel's AES-NI for
>> which gcc (and I think MSVC) have intrinsics, such as
>> _mm_aeskeygenassist_si128 ("the last argument must be an 8-bit
>> immediate").
>> There is no C++ way of doing this program with compile time loop
>> unrolling. If I would be able to do
>> Loop<0, 9>::operate([ ](constexpr unsigned int index) {
>> the_intrinsic(index); });
>> with the constexpr argument transformed into a non-type template
>> argument, then the problem would be solved.
>> I could also say static_assert(index < 10) and it would work too.
>
>
> Why wouldn't integral constants work? You can already write this:

You are one mail behind :D I just realized about this and posted it,
so let's see what the OP says.
> To view this discussion on the web visit
> https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/3810042a-253b-4aa0-84e9-72e7a79058b2%40isocpp.org.

Jakob Riedle

unread,
Apr 19, 2017, 9:49:03 AM4/19/17
to ISO C++ Standard - Future Proposals, talz...@gmail.com
Hello,

has there been any progress on this topic yet?

Thanks in advance
Jakob

mikael.p...@gmail.com

unread,
Aug 30, 2017, 7:23:21 AM8/30/17
to ISO C++ Standard - Future Proposals, talz...@gmail.com
Id like to see it, and it would be especially great for things like allowing static_assert. 

Myriachan

unread,
Sep 5, 2017, 3:28:15 PM9/5/17
to ISO C++ Standard - Future Proposals, talz...@gmail.com, mikael.p...@gmail.com
One use of constexpr parameters is to better represent processor intrinsics.  For example, _mm_shuffle_epi32 on x86 requires the shuffle parameter to be a compile-time constant, because the constant is encoded into the machine instruction.

Allowing constexpr parameters while also allowing overloading on constexpr parameters would be very useful in situations in which a compile-time implementation exists, but is less efficient if used at runtime.  A simple example is strlen:

std::size_t strlen(constexpr const char *s)
{
    for (const char *p = s; ; ++p)
    {
        if (*p == '\0')
        {
            return static_cast<std::size_t>(p - s);
        }
    }
}

std::size_t strlen(const char *s)
{
    __asm__("SSE 4.2 insanity");
}

Obviously, this isn't about strlen itself, because compilers already know to do this for the well-known functions.

Jakob Riedle

unread,
Sep 6, 2017, 5:35:20 AM9/6/17
to ISO C++ Standard - Future Proposals, talz...@gmail.com, mikael.p...@gmail.com
 A simple example is strlen

Nice! 

Another use case would be to give std::tuple an operator[]:

template<typename... Ts>
class tuple
{
   
/* ... */
   
   
const auto& operator[]( constexpr std::size_t index ) const {
       
return std::get<index>(*this);
   
}
};

bastie...@gmail.com

unread,
Sep 18, 2017, 6:03:35 PM9/18/17
to ISO C++ Standard - Future Proposals, talz...@gmail.com
I made an analogue proposal a while back but except for one positive opinion it didn't garner much attention.
My proposal is a counter proposal of sort: constexpr(true) specialization.
  • A constexpr(true) being a constexpr function/method that can't fallback into a runtime function.
  • can only call constexpr method & functions.
  • must be requested explicitly (constexpr assign, constexpr unary operator).
  • cannot issue linkage (no mangling for itself nor for the types that it uses internally), thereby solving the issue of literal non-type template parameters
  • all its arguments are constexpr and usable as such.
  • A runtime and a compile-time version of a same function for more efficient runtime and more powerful compile-time coding.
Now here is a detailed exemple.

//compile-time only function
//it will never have linkage
//all its arguments are constexpr
//a non-constexpr expression usage in its definition will cause an error instead of a fallback like normal constexpr
//is only called in an explicit constexpr context unlike normal constexpr
constexpr(true) auto func(Literal r) {}

//runtime specialization of func
/*constexpr(false)*/ auto func(Literal r) {}

//error redefinition of 'constexpr(false) void func(Literal)'
constexpr void func(Literal) {}

constexpr(true) auto func2();
//allowed
//constexpr is ignored, might warn, retains inline side effects
constexpr auto func2();

constexpr auto func3();
constexpr auto error_0 = func3();
//error: constexpr specialization of 'func3' after instanciation
constexpr(true) auto func3();


//addresses demands for partially constexpr argument-list
constexpr(true) void partial_constexpr(int constant)
{
   
return [](auto&&... runtime_args) /*normal constexpr lambda*/ {
       
//runtime code
       
//can use 'constant' as a constexpr here without capture
   
};
}

int main()
{
  
constexpr auto r_0 = func(Literal{}); //constexpr(true) version called
  
auto r_1 = func(Literal{}); //constexpr(false) version called

  partial_constexpr(42/*compile-time args*/)(/*runtime args*/);

  
auto r_2 = constexpr func(Literal{}); //also proposed unary constexpr operator for explicit selection, error if the expression is not constexpr
  
//is equivalent to
  
auto r_3 = []() { constexpr auto __ret__ = func(Literal{}); return __ret__; }(); 
}

//replacing of gnu's operator"" extension
constexpr(true) auto operator""_string_literal(const char *str, size_t len)
{
    
return []<size_t... Indexs>(std::index_sequence<Indexs...>&&)
    
{
       
return TemplateCharSeq<str[Indexs]...>{};
    
}(std::make_index_sequence<len>{});
}

//operator[] for std::tuple
template <class... Args>
struct tuple
{
   
...
   
constexpr(true) auto& operator[](size_t i) const
   
{
      
static_assert(< sizeof...(Args));

      
return std::get<i>(*this);
   
}


   
constexpr(true) auto& operator[](size_t i)
   {
      
static_assert(< sizeof...(Args));

      
return std::get<i>(*this);      
   
}

   
....
};

template <class... Args> struct Select : Args... { using Args::operator()...; };
template <class... Args> Select(Args&&...) -> Select<Args...>;

//for lambdas
void func_3()
{
  
constexpr auto f_0 = []() {}; //operator() is constexpr /*fallback on runtime*/, no changes
  
constexpr auto f_1 = []() constexpr(false) {}; //operator() cannot be called at compile-time
  
constexpr auto f_2 = []() constexpr(true) {}; //operator() can only be called a compile-time and explicitly

  
constexpr Select f_3 = { f_1, f_2 }; 

  
constexpr auto r_4 = f_3(); //f_2
  
auto r_5 = f_3(); //f_1
}

Here is the summary:

- extend constexpr specifier to allow it to take a constexpr explicit bool (not a constexpr expression unless if there are good use-cases for it) where:
  • constexpr(false) is the default specifier of a function/method
    • optional for functions/method.
    • if specified with lambdas allows not-constexpr lambda as lambdas are by default constexpr.
  • constexpr(true) is a constexpr only function.
    • no Assembly representation (no linkage nor mangling).
    • all arguments are constexpr.
    • error if non-constexpr expression is used (a constexpr fallback to runtime because of an overflow for instance).
    • only used in explicit constexpr context.
    • cannot be called before its specialization if specialization or else error.
  • constexpr
    • keeps all its properties and definition.
    • will still trigger redefinition error if previous constexpr(false) version previously defined.
    • constexpr attribute ignored if previous constexpr(true) is defined.
- unary constexpr operator to request a constexpr expression explicitly:
   auto t = constexpr expr; //will error if expr cannot resolve into a constexpr expression

The reason why I believe in this proposal:
  • drastically improve compile-time programming
  • no mangling issues, solves a lot of other proposal (a better is_constexpr, operator[] for std::tuple, getting rid of string literal gnu extension, etc...)
  • no new keyword.
  • zero runtime impact.
  • maintains backward compatibility.
  • gives more expressiveness for lambda

Jakob Riedle

unread,
Sep 25, 2017, 5:02:10 AM9/25/17
to ISO C++ Standard - Future Proposals, talz...@gmail.com, bastie...@gmail.com
Am Dienstag, 19. September 2017 00:03:35 UTC+2 schrieb bastie...@gmail.com:
I made an analogue proposal a while back but except for one positive opinion it didn't garner much attention.
My proposal is a counter proposal of sort: constexpr(true) specialization.
  • A constexpr(true) being a constexpr function/method that can't fallback into a runtime function.
  • can only call constexpr method & functions.
  • must be requested explicitly (constexpr assign, constexpr unary operator).
  • cannot issue linkage (no mangling for itself nor for the types that it uses internally), thereby solving the issue of literal non-type template parameters
  • all its arguments are constexpr and usable as such.
  • A runtime and a compile-time version of a same function for more efficient runtime and more powerful compile-time coding
Not a bad idea...
However, it does not allow someone to partially make a function constexpr.
If only the first parameter of a function needs to be constexpr, your methodology forces them all to be constexpr. This is unneccessarily restricted to my impression.
And furthermore I feel, the fact that function parameters have to be constexpr in order to invoke the funciton is more obviously conveyed if the parameters are being annotated with constexpr.
Because that's what is important for the user, not that the compiler doesn't need to generate assembly for it. Does that make sense?
This keeps the thing intuitive and has the same (or even greater opportunities as your solution since we then are able to overload on individual parameters depending on their constexpr-ness).
That is, given compilers are allowed to deduce the neccessity of linkage depending on whether all parameters are annotated 'constexpr'.

I still prever the annotation of parameters with 'constexpr' :|

Jakob

bastie...@gmail.com

unread,
Sep 25, 2017, 1:26:34 PM9/25/17
to ISO C++ Standard - Future Proposals, talz...@gmail.com, bastie...@gmail.com
Le lundi 25 septembre 2017 11:02:10 UTC+2, Jakob Riedle a écrit :
Not a bad idea...
However, it does not allow someone to partially make a function constexpr.
If only the first parameter of a function needs to be constexpr, your methodology forces them all to be constexpr. This is unneccessarily restricted to my impression.
Actually, it can emulated with the following:

constexpr(true) auto f(int const_expr)
{
   
return [](auto&&... runtime_args) //normal constexpr lambda, might produce linkage within the caller's context
   
{
         
//const_expr usable as a constexpr here
         
//whilst runtime_args are not
         
...
   
};
}


int main()
{
   
int i = 0;
    f
(42/*constexpr args here*/)(i/*runtime args here*/);
}

 
And furthermore I feel, the fact that function parameters have to be constexpr in order to invoke the funciton is more obviously conveyed if the parameters are being annotated with constexpr.
Because that's what is important for the user, not that the compiler doesn't need to generate assembly for it. Does that make sense?
This keeps the thing intuitive and has the same (or even greater opportunities as your solution since we then are able to overload on individual parameters depending on their constexpr-ness).
That is, given compilers are allowed to deduce the neccessity of linkage depending on whether all parameters are annotated 'constexpr'.

I still prever the annotation of parameters with 'constexpr' :|

Jakob

The thing is that partially constexpr arguments is just a syntax alternative to generalized non-type template arguments.
template<std::string_view I> void func(int runtime);
and
void func(constexpr std::string_view i, int runtime);
only differs in calling conventions.

The proposal N3413 aimed at allowing any constexpr value to be passed as a template non-type parameter, and every issues preventing this proposal are common with the partial constexpr proposal.
Mainly:
- operator== or bit-field analysis ?
- how to mangle operator== ?
- how to mangle for all platform ?

This proposal has also a unique issue:
 How do you take the address of a partially constexpr function ?

I rather have N3413 first as it doesn't solve any of its issues.
Reply all
Reply to author
Forward
0 new messages