Inline variables - superior of templates and constexpr.

712 views
Skip to first unread message

sasho648

unread,
Dec 16, 2014, 1:50:46 PM12/16/14
to std-pr...@isocpp.org
As the title says - I'm purposing the introduction of a new variables specifier - 'inline', which will indicate that their value is known at compile-time. [Literals aren't itself 'inline' variables, so an reference to 'inline' variable can't bound to literal.] The motivation of introducing it is enabling static checks at functions, native 'read-only public' class member specifiers and creating more optimized code.

Example of the first one can be given by using 'std::array' if we access an object of the type using the 'operator[]' and 'constexpr' with invalid index no compiler warning or even an error will be introduced:

    
array<int, 5> arr;
   
cout
<< arr[98] << endl; //compiles just fine

Life example.

You'll say - this is too obvious - no one will do such thing but let's imagine that we are going to access the array element, instead of a literal, by some constexpr function.

constexpr int EvaluateSpecialArrayIndex(int a)
{ return a * sizeof(int); }

cout
<< arr[EvaluateSpecialArrayIndex(4)] << endl;

Now things get complex, huh? In order to check if the array index is valid we should first multiply by hand 4 * sizeof(int) and compare it with the array size. You think now - hey but why don't you add a static_assert in the 'EvaluateSpecialArrayIndex' and check 'a' with the array size. But the truth is - that you can't do that as the parameter 'a' isn't required to be 'constexpr', neither the return value of the function. Example on valid 'constexpr' call with 'lvalue':


int a = rand() % 4; //lvalue

cout
<< arr[EvaluateSpecialArrayIndex(a)] << endl; // also valid

Life example.

So this piece of code is invalid:

constexpr int EvaluateSpecialArrayIndex(int a)
{ static_assert( a < 5, "invalid a" ); return a * sizeof(int); }


Life example.

Here is where 'inline' variables came in use. They can be initialized only once with another 'inline' variable or with literal but aren't 'const' and non-'inline' variables can't be casted to 'inline'. However the opposite is possible (casting from 'inline' to any other type). Functions declared with 'inline' parameters or return-value will be implicit 'inline' themselves. Instance to a function with argument literal will first search for one with parameter of type 'inline <type-of-literal>'. So now we could overload the 'std::array::operator[]' in order to do static checks on 'inline' variables like this (supposing that 'templates' are still used):

template<typename T, size_t sz>
T
& std::array::operator[]( size_type pos )

{
   
static_assert( pos < sz, "invalid array index" );

   
return this->operator[]((int)pos); //instance default 'non-inline' (run-time) operator[] with 'a' casted (from 'inline int') to 'int'
}


By using the above function this invalid code:

array<int, 5> arr;
   
cout
<< arr[98] << endl;

Will report compiler error, instead of producing undefined behavior.

The other usage of the specifier, optimizing code, can be demonstrated by creating one general function - for both 'inline' and 'non-inline' parameters, of certain algorithm and other 2 for 'inline' and 'non-inline' which will specialize it, so when you call it with for example literal - it will be evaluated at compile-time and when you do with some variable it will create code for run-time execution. Example:

auto SomeAlgo(inline bool bIsInline, typename( bIsInline ? inline int : int ) n) -> decltype(n)
{
   
//Do something with 'n' and return 'int' (or 'inline int')
}

inline int SomeAlgo(inline int a) { static_assert( a < 0, "invalid a" ); return SomeAlgo(true, a); }

int SomeAlgo(int a) { return SomeAlgo(false, a); }

Note that the first function is declared as returning an 'inline int', if it was the function itself to be 'inline', it should be 'int inline SomeAlgo', which is an inline function returning 'int'.

And some example instances of 'SomeAlgo':

SomeAlgo(rand()); //it will invoke code creation as 'rand()' doesn't return 'inline' value

SomeAlgo(9); //Will evaluate at compile-time as literal '9' can be used to initialize 'inline' variables

Another useful feature will be the use of 'inline references' which are such that will point at some compiler-known location (nothing special), they'll be useful when you want to create a public const reference to some private member in order to allow public read-only access to it. For now we could only write this:

class A
{
   
int _a;

public:

   
const int &a = _a;
} ;

Life example.

But in the case memory for 'a' will be allocated for each 'A' instance, which is not a deal. We couldn't write this with 'constexpr' for some strange reason:

class A
{
   
int _a;

public:

   
constexpr int &a = _a; // the same as 'const int & constexpr a' - still produces error
} ;

Life example.

By using our new construct this could write this:

class A
{
   
int _a;

public:

   
const int & inline a = _a; // inline reference to '_a'
} ;

Which basically means that each reference to 'a', will be evaluated like - '(const int)_a', so no additional memory will be created now.

Function specialization will be done to functions with 'inline' parameters, which when receive argument values will search for a function which have specialized them. The parameter specialization will be done via the '==' operator (one specialization can be for multiples values). Examples

auto FuncAdd(inline typename T, T a, T b) -> T //general function
{
   
return a + b;
}

auto FuncAdd(inline typename T == int, T a, T b) -> T //specialized function about the 'T' parameter, as no inline params, no implicit 'inline' conversion
{
    cout
<< "I can deal with int" << endl;

   
return a + b;
}

And some 'FuncAdd' instances:

FuncAdd(int, 9, 5); // 'FuncAdd' int specialization called (of type - 'auto (inline typename T == int, T a, T b) -> T')

FuncAdd(double, 3.14f, 1); // 'FuncAdd' general called (of type - 'auto (inline typename T, T a, T b) -> T')


Some additional 'inline' specifier rules connected to specialization and classes. A class can have special methods for 'inline' instances of it, which are defined by specifiying 'inline' after it's declaration (in such methods 'this' will be inline pointer and will point to an sequenced 'inline'  array holding it's inline member objects). Inline instances of class will instance only it's special 'inline-this' methods and won't create any memory for it's non-inline members. Classes can't be specialized but you can create a derived class which specialize their methods. But this could change on request.

So what do you think about it - does it have the potential to drop both 'template' and 'constexpr' as keywords?

Thiago Macieira

unread,
Dec 16, 2014, 2:51:27 PM12/16/14
to std-pr...@isocpp.org
On Tuesday 16 December 2014 10:50:46 sasho648 wrote:
> template<typename T, size_t sz>
> T & std::array::operator[]( size_type pos )
>
> {
> static_assert( pos < sz, "invalid array index" );
>
> return this->operator[]((int)pos); //instance default 'non-inline'
> (run-time) operator[] with 'a' casted (from 'inline int') to 'int'
> }

Did you forget the inline before size_type above?

Also, it seems to me that dropping the inlineness by a simple cast is a bad
idea. It should be preserved and propagated across uses so code further down
the line can benefit from compile-time checks too.

I think the code above might be better written instead as:

template<typename T, size_t sz>
T &std::array<T,sz>::operator[](size_type pos)
{
if (std::is_constexpr(pos))
static_assert(pos < sz, "invalid index");

// call internal, private function
return this->_Internal_get(pos);
}

Where std::is_consteexpr is similar to the GCC builtin __builtin_constant_p,
but ensures that the static_assert won't be tried if the condition failed.

That syntax would also allow:

constexpr uint32_t crc32(uint32 partial_crc32 uint64 data)
{
if (!std::is_constexpr(partial_crc32, data)) {
asm ("crc32 %1, %0" : "+r" (partial_crc32) : "r" (data));
} else {
// inline, constexpr implementation of CRC32
}
return partial_crc32;
}

Why you may want to do that, you ask? Because the inline & constexpr
implementation of CRC32 results in slower runtime code compared to the CPU
instruction, but is required because the compiler doesn't know what the inline
assembly does and is technically constexpr too.

> auto SomeAlgo(inline bool bIsInline, typename( bIsInline ? inline int : int
> ) n) -> decltype(n)
> {
> //Do something with 'n' and return 'int' (or 'inline int')
> }

Whoa, whole new syntax here. You should probably send a proposal for the new
syntax first, before we discuss inline variables.

> inline int SomeAlgo(inline int a) { static_assert( a < 0, "invalid a" );
> return SomeAlgo(true, a); }
>
> int SomeAlgo(int a) { return SomeAlgo(false, a); }
>
> *Note that the first function is declared as returning an 'inline int', if
> it was the function itself to be 'inline', it should be 'int inline
> SomeAlgo', which is an inline function returning 'int'.*

No go. You can't change that syntax now.

> So what do you think about it - does it have the potential to drop both
> 'template' and 'constexpr' as keywords?

No, I don't think so. But this is my opinion. That said, one aspect of what
you described solves something I want: the ability to have runtime code differ
from the constexpr implementation. One solution is what I've showed, with a
builtin intrinsic that allows me to check; another is to allow one to overload
a constexpr function with a non-constexpr one.

And when I hear "inline variables", by parallel with template variables, I
somehow think of something like an automatically-updated variable:

int i;
inline int j = i * 4; // no warning

i = 4;
std::cout << j << endl; // prints 16
i = 1;
std::cout << j << endl; // prints 4

template<typename T> inline int fourth = i / T(4);
std::cout << fourth<int> << endl; // prints 0
std::cout << fourth<float> << endl; // prints 0.25

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358

sasho648

unread,
Dec 16, 2014, 5:17:46 PM12/16/14
to std-pr...@isocpp.org
Did you forget the inline before size_type above?
 
 Yes - my bad sorry the first example is:

template<typename T, size_t sz>

T
& std::array::operator[]( inline size_type pos )


{
   
static_assert( pos < sz, "invalid array index" );

   
return this->operator[]((int)pos); //instance default 'non-inline' (run-time) operator[] with 'a' casted (from 'inline int') to 'int'
}

So it overrides 'std::array::operator[]' for 'inline''s (or compiler known constants).

I think the code above might be better written instead as:


template<typename T, size_t sz>
T &std::array<T,sz>::operator[](size_type pos)
{
        if (std::is_constexpr(pos))
                static_assert(pos < sz, "invalid index");

        // call internal, private function
        return this->_Internal_get(pos);
}

Where std::is_consteexpr is similar to the GCC builtin __builtin_constant_p,
but ensures that the static_assert won't be tried if the condition failed.

That syntax would also allow:

constexpr uint32_t crc32(uint32 partial_crc32 uint64 data)
{
        if (!std::is_constexpr(partial_crc32, data)) {
                asm ("crc32 %1, %0" : "+r" (partial_crc32) : "r" (data));
        } else {
                // inline, constexpr implementation of CRC32
        }
        return partial_crc32;
}

Why you may want to do that, you ask? Because the inline & constexpr
implementation of CRC32 results in slower runtime code compared to the CPU
instruction, but is required because the compiler doesn't know what the inline
assembly does and is technically constexpr too.

In your example the compiler should execute each instance of 'crc32' which it's not required to, he could just replace instances to the function into an assembly 'call' to it's code (which will be strange one as it should require additional compiler work). This reminds me that 'inline' functions act the same way so adding 'inline' parameters or return-value to a function will immediately require the compiler to evaluate it at each instance, the same way as 'template''s are (so the functions won't be only 'inline'). But even this - I believe filtering your function request at instance time is better then doing it in some 'if''s.

> auto SomeAlgo(inline bool bIsInline, typename( bIsInline ? inline int : int 
> ) n) -> decltype(n) 
> { 
>     //Do something with 'n' and return 'int' (or 'inline int') 
> } 
Whoa, whole new syntax here. You should probably send a proposal for the new 
syntax first, before we discuss inline variables. 

Only a little - as I said 'inline' variables can be used instead of templates, so this is equivalent to something like:

template<bool bIsInline, typename T = (bIsInline ? double : int)>
auto SomeAlgo(T n) -> decltype(n)

But this doesn't compile either for some strange reasons.

And when I hear "inline variables", by parallel with template variables, I 
somehow think of something like an automatically-updated variable: 
        int i; 
        inline int j = i * 4;                        // no warning 
        i = 4; 
        std::cout << j << endl;                // prints 16 
        i = 1; 
        std::cout << j << endl;                // prints 4 
        template<typename T> inline int fourth = i / T(4); 
        std::cout << fourth<int> << endl;        // prints 0 
        std::cout << fourth<float> << endl;        // prints 0.25 

I believe this can be better implemented using a function.

Tony V E

unread,
Dec 16, 2014, 5:43:55 PM12/16/14
to Andrzej Krzemieński
On Tue, Dec 16, 2014 at 1:50 PM, sasho648 <sash...@mail.bg> wrote:
Here is where 'inline' variables came in use. They can be initialized only once with another 'inline' variable or with literal but aren't 'const' and non-'inline' variables can't be casted to 'inline'. However the opposite is possible (casting from 'inline' to any other type). Functions declared with 'inline' parameters or return-value will be implicit 'inline' themselves. Instance to a function with argument literal will first search for one with parameter of type 'inline <type-of-literal>'. So now we could overload the 'std::array::operator[]' in order to do static checks on 'inline' variables like this (supposing that 'templates' are still used):

template<typename T, size_t sz>

T
& std::array::operator[]( inline size_type pos )


{
   
static_assert( pos < sz, "invalid array index" );

   
return this->operator[]((int)pos); //instance default 'non-inline' (run-time) operator[] with 'a' casted (from 'inline int') to 'int'
}



Wouldn't 'constexpr' be the better keyword?  So you can write 2 versions of the function - one for constexpr (compile-time) params, one for runtime params.

Tony

Ville Voutilainen

unread,
Dec 16, 2014, 5:45:43 PM12/16/14
to std-pr...@isocpp.org
Good luck. constexpr as a function parameter specifier, and
suggestions to be able
to overload on constexpr, has been shot down every time that idea has
been floated.

sasho648

unread,
Dec 16, 2014, 5:57:31 PM12/16/14
to std-pr...@isocpp.org
@Tony V E 

This doesn't matter - I'm just showing that ISO C++ standard introduces new keywords like every-day - 'constexpr' is absolutely useless and as I just showed it can be completely replaced with 'inline'. Even when I want to declare that the 'return' value of a function will be inline (not the whole function) I can still do it without using additional keywords like this:

int (inline Func)();

auto Func() -> inline int;


Those new keywords break compatibility very bad, if before 'C++ 11' this was a valid expression:

int constexpr = 67 * 8;

Now it should report compiler error. Those fat asses there should get more productive and doesn't introduce breaking-code new keywords for such bad and not-well implemented ideas.

@Ville Voutilainen 

Why - again is the fatness or something else?

Ville Voutilainen

unread,
Dec 16, 2014, 6:09:25 PM12/16/14
to std-pr...@isocpp.org
On 17 December 2014 at 00:57, sasho648 <sash...@mail.bg> wrote:
> @Tony V E
>
> This doesn't matter - I'm just showing that ISO C++ standard introduces new
> keywords like every-day - 'constexpr' is absolutely useless and as I just

Useless? It tells the user what to expect, it allows an implementation to know
when it's expected to evaluate at compile-time and when not.

> showed it can be completely replaced with 'inline'. Even when I want to

inline doesn't convey the same semantics.

> Those new keywords break compatibility very bad, if before 'C++ 11' this was
> a valid expression:
>
> int constexpr = 67 * 8;

"very bad"? How much code have you ever found that did that?

> Now it should report compiler error. Those fat asses there should get more
> productive and doesn't introduce breaking-code new keywords for such bad and
> not-well implemented ideas.

Well, I guess the "bad" and "not-well implemented" parts are debatable.

> Why - again is the fatness or something else?

As far as I can understand, constexpr function arguments would be against
the design of how constant expressions are to be evaluated. If you have
something productive to say about constexpr, feel free to write proposals
improving it. Otherwise I recommend minding your tone if you want to
be taken seriously.

Johannes Schaub

unread,
Dec 16, 2014, 6:14:58 PM12/16/14
to std-pr...@isocpp.org
2014-12-16 22:57 GMT+00:00 sasho648 <sash...@mail.bg>:
> @Tony V E
>
> This doesn't matter - I'm just showing that ISO C++ standard introduces new
> keywords like every-day - 'constexpr' is absolutely useless and as I just
> showed it can be completely replaced with 'inline'. Even when I want to
> declare that the 'return' value of a function will be inline (not the whole
> function) I can still do it without using additional keywords like this:
>
> int (inline Func)();
>
> auto Func() -> inline int;
>
>
> Those new keywords break compatibility very bad, if before 'C++ 11' this was
> a valid expression:
>
> int constexpr = 67 * 8;
>

Oh, the only real-world code that comes to mind that uses "constexpr"
in "incompatible" ways is my second example here:
http://stackoverflow.com/a/6473250/34509

Ville Voutilainen

unread,
Dec 16, 2014, 6:17:13 PM12/16/14
to std-pr...@isocpp.org
On 17 December 2014 at 01:14, Johannes Schaub
<schaub....@googlemail.com> wrote:
> Oh, the only real-world code that comes to mind that uses "constexpr"
> in "incompatible" ways is my second example here:
> http://stackoverflow.com/a/6473250/34509


No offense intended, Johannes, but I'm not QUITE sure whether that counts as
real-world code. :D
Message has been deleted

David Krauss

unread,
Dec 17, 2014, 4:11:25 AM12/17/14
to std-pr...@isocpp.org
On 2014–12–17, at 5:08 PM, sasho648 <sash...@mail.bg> wrote:

int (inline Func)()

auto Func() -> inline int

Is equivalent to this:

constexpr Func()

This may be the least sensible thing I’ve yet read on this list.

sasho648

unread,
Dec 17, 2014, 4:15:01 AM12/17/14
to std-pr...@isocpp.org
Useless? It tells the user what to expect, it allows an implementation to know 
when it's expected to evaluate at compile-time and when not. 

The same way as 'inline' could tell but not for the whole function, making things ugly - instead separately for the return-value or the parameters without introducing new keyword. Here is how 'constexpr'  function can be simulated to do the exact same thing:

int (inline Func)()

auto Func() -> inline int

Is equivalent to this:

constexpr Func()

"very bad"? How much code have you ever found that did that? 

Who knows - it's not something that can't be written. But as we have a choice - why introduce new keywords instead of recycling old-ones?

Well, I guess the "bad" and "not-well implemented" parts are debatable. 

Let's debate them then. I just showed why 'constexpr''s are not-well implemented by my first post examples which shows some of their limits (automated static checks, explicit optimizations and inline references).

As far as I can understand, constexpr function arguments would be against 
the design of how constant expressions are to be evaluated. If you have 
something productive to say about constexpr, feel free to write proposals 
improving it. Otherwise I recommend minding your tone if you want to 
be taken seriously. 

Sorry then - that I called what is obvious. How can you introduce a new-keyword only to say that some functions MAY be evaluated at run-time and not implement natural things which everyone who have ever heard of 'constexpr' have tried without a luck? And what is this thing against my proposal - can someone name it more clearly?

sasho648

unread,
Dec 17, 2014, 4:20:13 AM12/17/14
to std-pr...@isocpp.org
The declaration is needed to be so complex, because the use of 'inline' before the function is reserved for 'inline' functions. However for the case you may use type aliases the same way you do for functions returning array references or pointers. Example:

using FuncReturnType = inline int;

FuncReturnType Func();


Douglas Boffey

unread,
Dec 17, 2014, 9:25:23 AM12/17/14
to std-pr...@isocpp.org
Am I to understand the “inline typename T” as an alternative method of expressing templates or one function that will work for any type?
 
Firstly, whether or not you want this as a template replacement, I object to having to specify the type when the compiler can deduce that from the variables.
 
If it is meant as a replacement for a template, having the type as a parameter is confusing… I would expect one function, and only one function, to be substantiated when a function is not explicitly templated.
 
If it is not, I am at a loss as to how a compiler is meant to implement it efficiently (or at all, if it is defined in a separate TU).
 
Regarding the second (overloaded) function, is that supposed to overload on type int?  If so, “auto FuncAdd(int, …” would be more in line with current practice.  This will not create an ambiguity, as the parameter is already defined as a typename.
 
And some 'FuncAdd' instances:

FuncAdd(int, 9, 5); // 'FuncAdd' int specialization called (of type - 'auto (inline typename T == int, T a, T b) -> T')

FuncAdd(double, 3.14f, 1); // 'FuncAdd' general called (of type - 'auto (inline typename T, T a, T b) -> T')


Some additional 'inline' specifier rules connected to specialization and classes. A class can have special methods for 'inline' instances of it, which are defined by specifiying 'inline' after it's declaration (in such methods 'this' will be inline pointer and will point to an sequenced 'inline'  array holding it's inline member objects). Inline instances of class will instance only it's special 'inline-this' methods and won't create any memory for it's non-inline members. Classes can't be specialized but you can create a derived class which specialize their methods. But this could cha
...

sasho648

unread,
Dec 17, 2014, 1:12:46 PM12/17/14
to std-pr...@isocpp.org
Am I to understand the “inline typename T” as an alternative method of expressing templates or one function that will work for any type?

It's an alternative to templates, as I said at the beginning functions with either inline parameters or return-value will be implicitly 'inline' and also should be executed by the compiler, the same way as general template's are. 

Firstly, whether or not you want this as a template replacement, I object to having to specify the type when the compiler can deduce that from the variables.

Automated 'type' deduction is being better implemented by using 'auto' keyword as function parameter or return-value. Like this:

auto FuncAdd(auto a, auto b) //also general function, which have parameters and return-value of any type
{
   
return a + b;
}

Inline arguments are meant to be used and not automatically deduced. You may for example call the function 'FuncAdd', I declared above with first argument of type 'int' and 'a', 'b' from type double - in which case they should be converted. Example:

double a, b;

FuncAdd(int, a, b); //here 'a' and 'b' will be implicitly converted into 'int'


Sometimes we want to specify some setting on function local instead of parameter in which case 'auto' won't do our job. Examples:

void SomeFunc(inline typename T, inline size_t sz)
{
    T arr
[sz]; //create some local array with specified inline size and type

}


Regarding the second (overloaded) function, is that supposed to overload on type int?  If so, “auto FuncAdd(int, …” would be more in line with current practice.  This will not create an ambiguity, as the parameter is already defined as a typename.

You're absolutely right - we could create the 'int' overload of the function just like you said but using templates is the same - you aren't required to specialize them but instead create non-template function with the needed type. I just wanted to show that the same behavior as 'template''s specializing is possible (but not required). Examples of the above using templates:

template<typename T>
T
FuncAdd(T a, T b)
{
   
return a + b;
}


int FuncAdd(int a, int b)

{
    cout
<< "I can deal with int" << endl;
   
   
return a + b;
}


template<>
int FuncAdd(int a, int b)
{
    cout
<< "I can deal with int, template" << endl;
   
   
return a + b;
}

As you can see there is a function template specialization of 'int' and a normal function which just have parameters of the type. In the case if we instance 'FuncAdd' with int's the non-template function will be executed.

Life example.

As you can clearly see those 'template''s only add additional code complexity. I believe it's way more natural to use inline function arguments.

sasho648

unread,
Dec 18, 2014, 2:10:11 PM12/18/14
to std-pr...@isocpp.org
Recently a new idea came up into my mind. Why don't we allow 'inline' (or compiler known variables) to be editable, if they're declared in a function scope (or when they're locals) ? This could enable us to write compile-time executed loops which give us the possibility for loop unrolling. Before I show some examples of the above, let me clarify that 'inline' declared variables now won't be uneditable, so they are allowed only in function definitions, globals can be only 'const inline' which means that they're compiler-time known but not-editable. This also means that another special class methods for 'const inline' variables should be allowed for classes. When talking for classes I remember that in my first post wrote this "in such methods 'this' will be inline pointer and will point to an sequenced 'inline'  array holding it's inline member objects" (talking for 'inline' class functions), which is now false - such methods will act similar to ones specifying 'const' objects in such way that member objects, and 'this' pointer will became implicitly 'inline' or 'const inline', depending what object the special method describes. Editing 'inline' variables can be done only in control-flow, defined by other inlines. The examples:

inline int vGlobal; //error: modifiable 'inline''s can be declared only in functions

const inline int vGlobal; //ok - 'const inline' can be global

void SpeedFunc(int (&arr)[5])
{
   
for(inline size_t i(0); i < sizeof(arr) / sizeof(arr[0]); ++i) //this loop will be evaluated at run-time

        arr
[i] = 0;

   
/*
    //Equivalent version of the code above, without using inlines

    arr[0] = 0;

    arr[1] = 0;

    arr[2] = 0;

    arr[3] = 0;

    arr[4] = 0;

    */

}

void WrongFunction(bool b)
{
   
inline int a;

   
if(b) a = 9; //illegal, the assignment of inline 'a', depends on non-inline
}


Can somebody give an opinion about it? Does this idea worth implementing and can it be proposed?

TC

unread,
Dec 18, 2014, 5:59:01 PM12/18/14
to std-pr...@isocpp.org
What's the point, trying to beat the optimizer?

Loop unrolling is not necessarily a good thing (increases code size, which in turn can cause increased cache misses). The compiler has a much better idea about which loops should be unrolled than the programmer.

Pretty much everything being proposed in this thread provides no additional expressiveness or efficiency; it would at most add a different way of doing the same things we can already do now. Whether or not templates as they are now would be adopted if we were designing a language from scratch, it will be around for a long, long time, for compatibility reasons. The costs of adding such a complicated feature vastly outweighs any benefit that can be gained from it.

sasho648

unread,
Dec 18, 2014, 7:18:13 PM12/18/14
to std-pr...@isocpp.org
And how can you check compiler-known values validity at compile-time. Do you like being notified for them as an undefined behavior after the program starts executing. My first examples showed this very clearly.

constexpr int EvaluateSpecialArrayIndex(int a)
{ return a * sizeof(int); }

array
<int, 5> arr;
   
cout
<< arr[98] << endl; //compiles just fine, and produces undefined behavior

cout
<< arr[EvaluateSpecialArrayIndex(4)] << endl; //the same as above


If you call no additional functionality being able to prevent this - I really don't know what it could be?

Allowing define-like references, that doesn't require run-time memory allocation is not efficiency?

I believe that enabling this construct will allow us to do a lot more powerful things, like creating types on the fly but - you don't care I guess. Let me first give you an example. If we want to introduce a 'enum' names ending with ascending number we could write something like:

auto CreateEnumAscendingNumber(inline std::string strNamesBase, inline int Range) -> inline enum
{
   
inline enum EnTmp;

   
for(inline int i(0); i < Range; ++i)
       
EnTmp += (strNamesBase + Range);

     
return EnTmp;
}

Now if we need to create an enum with members "MEMBER_0", "MEMBER_1" ... "MEMBER_10" - we can just instance the above function:

enum EN_AUTO_CREATED = CreateEnumAscendingNumber("MEMBER_", 10);

This would be very powerful and saving-time feature. For now you don't have any other option then writing the enum yourself, by hand. This feature can't be added in 'constexpr' functions because they aren't specialized to execute at compile-time. Haven't I still convinced you. Do you still think that it's just one useless feature. Then better continue write everything by hand or use the ugly preprocessor and let your compile-time errors to surprise you with undefined behavior at run-time - it's your choice.

Let me tell you something about templates - they have complex syntax to follow and are hard to learn in contrast to my 'inline' variables which are a lot simpler and just fit into any other code, unlike templates which need an entirely different syntax to follow. I believe C++ students will learn them easier.

TC

unread,
Dec 18, 2014, 7:56:03 PM12/18/14
to std-pr...@isocpp.org


On Thursday, December 18, 2014 7:18:13 PM UTC-5, sasho648 wrote:
And how can you check compiler-known values validity at compile-time. Do you like being notified for them as an undefined behavior after the program starts executing. My first examples showed this very clearly.

constexpr int EvaluateSpecialArrayIndex(int a)
{ return a * sizeof(int); }

array
<int, 5> arr;
   
cout
<< arr[98] << endl; //compiles just fine, and produces undefined behavior

cout
<< arr[EvaluateSpecialArrayIndex(4)] << endl; //the same as above


If you call no additional functionality being able to prevent this - I really don't know what it could be?

If you are really paranoid, use the tuple-like interface with std::get. I strongly suspect that most out-of-bounds access do not use compile-time constants anyway.
 

Allowing define-like references, that doesn't require run-time memory allocation is not efficiency?

I believe that enabling this construct will allow us to do a lot more powerful things, like creating types on the fly but - you don't care I guess. Let me first give you an example. If we want to introduce a 'enum' names ending with ascending number we could write something like:

auto CreateEnumAscendingNumber(inline std::string strNamesBase, inline int Range) -> inline enum
{
   
inline enum EnTmp;

   
for(inline int i(0); i < Range; ++i)
       
EnTmp += (strNamesBase + Range);

     
return EnTmp;
}

Now if we need to create an enum with members "MEMBER_0", "MEMBER_1" ... "MEMBER_10" - we can just instance the above function:

enum EN_AUTO_CREATED = CreateEnumAscendingNumber("MEMBER_", 10);


Compile-time std::strings and number-to-string conversion? (Hint, adding an int to a std::string doesn't do what you think it does.) Did you even think about implementability?

Not to mention the question "why on earth would you want such an enum?" And even if you do, you can generate such an enum in your favorite scripting language in a few minutes in same or fewer lines of code, throw it into a header and never have to worry about it ever again. And it would actually play much nicely with IDEs.
 
This would be very powerful and saving-time feature. For now you don't have any other option then writing the enum yourself, by hand. This feature can't be added in 'constexpr' functions because they aren't specialized to execute at compile-time. Haven't I still convinced you. Do you still think that it's just one useless feature. Then better continue write everything by hand or use the ugly preprocessor and let your compile-time errors to surprise you with undefined behavior at run-time - it's your choice.

 
Let me tell you something about templates - they have complex syntax to follow and are hard to learn in contrast to my 'inline' variables which are a lot simpler and just fit into any other code, unlike templates which need an entirely different syntax to follow. I believe C++ students will learn them easier.

They would have to learn both the current template syntax and your invented ones. And your syntax is neither "easier" nor "simpler" in any significant way. 

Douglas Boffey

unread,
Dec 18, 2014, 8:04:31 PM12/18/14
to std-pr...@isocpp.org
It seems there is some confusion: a function template is a SET of
functions, so should have a different syntax.
> --
>
> ---
> 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/.
>

Thiago Macieira

unread,
Dec 18, 2014, 8:53:27 PM12/18/14
to std-pr...@isocpp.org
On Thursday 18 December 2014 16:18:13 sasho648 wrote:
> constexpr int EvaluateSpecialArrayIndex(int a)
> { return a * sizeof(int); }
>
> array<int, 5> arr;
>
> cout << arr[98] << endl; //compiles just fine, and produces undefined
> behavior

How is that different from the following?

int arr[5];
cout << arr[98] << endl;

Both compile just fine and produce UB.

sasho648

unread,
Dec 19, 2014, 4:03:38 AM12/19/14
to std-pr...@isocpp.org
@Thiago Macieira '

It's different in that 'std::array' is a class and could provide such constant checks for it's index, if an overload of operator[] is provided with 'inline' argument.

@TC

Compile-time std::strings and number-to-string conversion? (Hint, adding an int to a std::string doesn't do what you think it does.) Did you even think about implementability?
Not to mention the question "why on earth would you want such an enum?" And even if you do, you can generate such an enum in your favorite scripting language in a few minutes in same or fewer lines of code, throw it into a header and never have to worry about it ever again. And it would actually play much nicely with IDEs.

But you got the idea, right? The code wouldn't compile even if I have used the proper 'string' to 'int' conversion. I don't know why everyone doesn't care about meta-programming?

They would have to learn both the current template syntax and your invented ones. And your syntax is neither "easier" nor "simpler" in any significant way. 

Really - so you're saying that this:

template<typename T, size_t sz>
void SomeFunc()
{
    T tmpArr[sz];
}

Is easier to learn with all this new syntax, including different brackets and new keyword 'template' and that the below is harder, no matter that it fits into previously learned about function parameters and their syntax?

void SomeFunc(inline typename T, inline size_t sz)
{

    T tmpArr
[sz];
}

Ok - as you say.

@Douglas Boffey 

A function template defines general functions. You're talking about specialization, which is a different thing and as you pointed out above - it can be replaced with just a new function. 
But if you meant that 'template' are expanded, during compile-time - my functions with 'inline' parameters or return-value will do the same.

David Krauss

unread,
Dec 19, 2014, 4:26:08 AM12/19/14
to std-pr...@isocpp.org

On 2014–12–19, at 5:03 PM, sasho648 <sash...@mail.bg> wrote:

Is easier to learn with all this new syntax, including different brackets and new keyword 'template' and that the below is harder, no matter that it fits into previously learned about function parameters and their syntax?

void SomeFunc(inline typename T, inline size_t sz)
{
    T tmpArr[sz];
}

Just a suggestion: it might fit better with the existing language to merely allow template parameters to be deduced from function parameter values. Currently, a function parameter name is not allowed to alias a function template parameter name. The case of aliasing could be defined as a deduced context.

template< typename elem, std::size_t size >
void some_func( std::size_t size ) {
    std::array< elem, size > tmp;
}

some_func< int >( 3 ); // calls some_func< int, 3 >
some_func< int >( 5 ); // calls some_func< int, 5 >
int x = 4;
some_func< int >( x ); // error: need a constant expression
some_func< int, 5 >( 6 ); // error: mismatched values

auto * x = & some_fun< int, 10 >; // OK

The main downside I see is that the name in the function parameter declaration is semantically significant.

As for literal types that aren’t currently allowed as template non-type parameters, I have some faith that they’re on the way, at least as far as the ones for which it would make sense to instantiate such functions at all.

sasho648

unread,
Dec 19, 2014, 4:53:46 AM12/19/14
to std-pr...@isocpp.org
And what's the benefit from this suggestion as we still can't distinguish lvalues and compiler-constants, or did you have some other intend in mind? In every case if you meant the first we can't overload with non-template functions as it'll fully replace the template ones. Example:

template< std::size_t size >

void some_func( std::size_t size ) {

   
static_assert(size < 10, "invalid size");


}

void some_func( std::size_t size ) {

   
//Do Something with size
}

int x = 4;

some_func
(9); //calls some_func( std::size_t size )

some_func
(x); //same as above

Otherwise I would say that 'template' deduction at all is one bad joke. It deduces the type of some template argument by using an argument depending on it, which I believe is one infinitive loop (if not being badly interpreted by ISO C++). Example

template<typename T>
void Func(T a);

Func(9);


Here is how I interpret the above example:

Template parameter 'T' will be deduced from parameter 'a' type which is 'T' which will be deduced from parameter 'a' type... and etc. It's really non-sense. But fortunate the 'auto' keyword was added which probably in C++14 will allow this (a lot more sensitive):

void Func(auto a);

Func(9); //'a' type is deduced from the passed argument

At least something good to be added in ISO C++.


But let's talk about my proposal.

Ville Voutilainen

unread,
Dec 19, 2014, 5:12:40 AM12/19/14
to std-pr...@isocpp.org
On 19 December 2014 at 11:26, David Krauss <pot...@gmail.com> wrote:
> Just a suggestion: it might fit better with the existing language to merely
> allow template parameters to be deduced from function parameter values.
> Currently, a function parameter name is not allowed to alias a function
> template parameter name. The case of aliasing could be defined as a deduced
> context.
> template< typename elem, std::size_t size >
> void some_func( std::size_t size ) {
> std::array< elem, size > tmp;
> }

This seems quite related to
http://cplusplus.github.io/EWG/ewg-closed.html#31

David Krauss

unread,
Dec 19, 2014, 10:13:41 AM12/19/14
to std-pr...@isocpp.org
I’m happy with that general direction, too.

I guess we will continue to see such proposals get shot down. It’s really just a matter of educating folks about function template parameters. FWIW, std::integral_constant is a fair workaround for avoiding explicit template argument lists.


On 2014–12–19, at 5:53 PM, sasho648 <sash...@mail.bg> wrote:

And what's the benefit from this suggestion as we still can't distinguish lvalues and compiler-constants, 

Value category is unrelated to compile-time determination. A constexpr object is an lvalue with a compile-time value. The result of an arithmetic operation is an rvalue with a runtime value. We can discriminate on value category using forwarding references. Discriminating compile-time from runtime evaluation is a recipe for unmaintainable code. You don’t want to be writing functions twice and maintaining them in parallel.

or did you have some other intend in mind?

Feasibility? Ability to reason about function identity?

In every case if you meant the first we can't overload with non-template functions as it'll fully replace the template ones. Example:

template< std::size_t size >

void some_func( std::size_t size ) {
    static_assert(size < 10, "invalid size");

}

void some_func( std::size_t size ) {
    //Do Something with size
}

int x = 4;

some_func(9); //calls some_func( std::size_t size )

some_func(x); //same as above

I never mentioned rules that would make it so. You’re just inventing problems to avoid adopting someone else’s idea.

If you want that example to work, just say that the template can be chosen in the first instance because it’s more specialized than the non-template function. In the second instance, the template is not viable so it can be ignored.

Template parameter 'T' will be deduced from parameter 'a' type which is 'T' which will be deduced from parameter 'a' type... and etc. It's really non-sense. But fortunate the 'auto' keyword was added which probably in C++14 will allow this (a lot more sensitive):

C++14 is already standardized. (Note, there are less than two weeks remaining in 2014.)

void Func(auto a);

Func(9); //'a' type is deduced from the passed argument

At least something good to be added in ISO C++.


This is scheduled for the Modules TS if I heard right. It’s shorthand for your previous example and it makes exactly the same degree of sense.

ville.vo...@gmail.com

unread,
Dec 19, 2014, 10:30:01 AM12/19/14
to std-pr...@isocpp.org
Minor correction: Concepts TS, not modules.

Thiago Macieira

unread,
Dec 19, 2014, 12:48:47 PM12/19/14
to std-pr...@isocpp.org
On Friday 19 December 2014 23:13:27 David Krauss wrote:
> Discriminating compile-time from runtime evaluation is a recipe for
> unmaintainable code. You don’t want to be writing functions twice and
> maintaining them in parallel.

Yes, we do. Granted, for a handful of functions, but we do.

sasho648

unread,
Dec 19, 2014, 1:05:10 PM12/19/14
to std-pr...@isocpp.org
@Thiago Macieira 

Thanks for the support - I was really starting to think that everybody here are mentally sick. Thank you again.


@All other

You don't care that my construction is twice as easy to use and understand than 'template''s and 'constexpr''s. You don't care it helps solving one very big problem (static checks for function parameters).
You don't care it could safe you hand-writing code. You don't care that it could help explicitly optimizing some code. All you care for is one rubbish idea written for 2mins which doesn't add anything. Tell me,
David Krauss, how your genius idea helps us, what the heck is have in common with my proposal?

Ville Voutilainen

unread,
Dec 19, 2014, 1:12:54 PM12/19/14
to std-pr...@isocpp.org
On 19 December 2014 at 20:05, sasho648 <sash...@mail.bg> wrote:
> @Thiago Macieira
>
> Thanks for the support - I was really starting to think that everybody here
> are mentally sick. Thank you again.

Ah, one of those again. In case your ultimate goal is to make sure
your writings are not
taken seriously, I should report that thus far you're succeeding magnificently.

sasho648

unread,
Dec 19, 2014, 1:18:55 PM12/19/14
to std-pr...@isocpp.org
Ah, can you please not post here - if you have nothing to say about my proposal.

Ville Voutilainen

unread,
Dec 19, 2014, 1:23:24 PM12/19/14
to std-pr...@isocpp.org
On 19 December 2014 at 20:18, sasho648 <sash...@mail.bg> wrote:
> Ah, can you please not post here - if you have nothing to say about my
> proposal.

I haven't seen a proposal yet. A proposal has design rationale and motivation,
and assesses the overall fitting of the extension into the existing language
design, considers implementability, and explains why the extension is
superior to its alternatives. A proposal also does that without
flinging excrement
and making unfounded statements about the mental health of people much
wiser than you.

sasho648

unread,
Dec 19, 2014, 1:37:03 PM12/19/14
to std-pr...@isocpp.org
A proposal have those things after being boiled-down. Here we are just discussing it. My motivation was mentioned more than twice in this discussion. It seems however that you can't figure out arguments against it other than that 'why we need to easy our lives by skip writing some code?', 'why do we need an easy way of writing 'general functions'?', 'why do we need to optimize some programs as compiler does just fine?' (as I clearly showed an example where it doesn't), 'why do we need to detect program errors at compile-time as UB in run-time is just fine?'.

Ville Voutilainen

unread,
Dec 19, 2014, 1:43:48 PM12/19/14
to std-pr...@isocpp.org
Well, if you want to actually write a proposal, it seems you should answer those
questions in your proposal.

It's not about whether we can or cannot figure out arguments against
the proposal.
The proposal must explain why it should be adopted, or even considered.

Thiago Macieira

unread,
Dec 19, 2014, 1:52:29 PM12/19/14
to std-pr...@isocpp.org
On Friday 19 December 2014 10:05:09 sasho648 wrote:
> You don't care that my construction is twice as easy to use and understand
> than 'template''s and 'constexpr''s. You don't care it helps solving one
> very big problem (static checks for function parameters).

If it were twice as easy, we would care. It's not twice as easy to understand.
First it seemed like you wanted to do something in-between constexpr and
runtime, then you started adding new syntax, inline typename, etc. and
modifying the program's structures like creating new enum types. That's not
easier, that *more* *complex* than existing solutions.

And moreover, you have not described fully what you want. Break it down into
its component parts, describe the syntax, where it would exist, etc.

And do not insult people. You will only get ignored if you insult people.
Assume everyone in this list is smarter than you and write accordingly. Treat
their replies as decent and honest requests for more information and feedback
on your solution.

> You don't care it could safe you hand-writing code. You don't care that it
> could help explicitly optimizing some code. All you care for is one rubbish
> idea written for 2mins which doesn't add anything. Tell me,
> David Krauss, how your genius idea helps us, what the heck is have in
> common with my proposal?

sasho648

unread,
Dec 19, 2014, 4:54:49 PM12/19/14
to std-pr...@isocpp.org
Haven't I explained yet. It seems I have to write my motivation on every second post. Here we go again.

  1. Preventing UB at run-time by reporting invalid parameters at compile-time.

    Imagine we have a 'Matrix' class with constructor taking 2 parameters - x and y. We don't want our 'Matrix' objects to have zero dimensions, so we must restrict the parameter values somehow. For now this is only possible by using 'if' statements but they're evaluated at run-time. Since our matrices can be created both by compiler-known values and variables we can't use templates. Illustration:

    class Matrix
    {
       
    int x_size;
       
    int y_size;


    public:
       
    Matrix(int width, int height):
            x_size
    {width},
            y_size
    {height}
       
    { if(!x_size || !y_size) throw; }
       
    Matrix() = delete;
    };


    The problem of the above class is the error reporting for invalid compiler-known constructor parameters which happens later at run-time, if the function is ever instanced on the platform you're testing. It's been addressed long-time and partial solution is provided by using the preprocessor.

    Example:

    Matrix a(0, 0); //wouldn't report compiler error but instead will fail at run-time

    int i, j;

    Matrix a1(j, i); //showing that constructor would take variables too


    The lack of detecting such compiler-time known errors is shown best by the 'std::array' class:

    array<int, 5> arr;

    cout
    << arr[98] << endl; //compiles without error


  2. Avoid reserving memory for struct members which reference compiler known variables locations.

    Imagine a class with a member array of some complex structure, as each of it's elements have some pre-defined meaning and we want to direct access some of it's element structure member. For the purpose we decide to create a reference to the desired location:


  3. struct B;

    struct S {    

        B arr
    [5];  
         
       
    int &NumberOfSomething = arr[3].FieldName;
    };

    Each object created with the above structure will allocate space to store the reference 'NumberOfSomething', no matter that the location of the referenced object can be calculated at compile-time ('arr[3].FieldName').

    Life example.
The solution of the above problems is adding a new variable specifier - 'inline'. It will specify that the variable value is compiler-known and no run-time is allocated for them. Such variables can be assigned only by other variables with the same specifier or literals. Operations on such variables will be executed at compile-time. They can be declared 'const' which means that are un-editable after initialization. Only such (declared as 'const') can be declared in the global space. Functions parameter or return-value can also be declared as 'inline' in which case the function itself will became implicitly inline and each instance of it should be evaluated at compile-time. Inline variables can name a type if they are declared with 'class', in which case they can be used as a type in declarations.

As a solution for the second problem I suggest inline references. They have the same rules as normal inlines with the exception that can't be assigned literals but object names.

So the first problem can be solved like:

class Matrix
{
   
int x_size;
   
int y_size;


public:
   
Matrix(int width, int height):
        x_size
{width},
        y_size
{height}
   
{ if(!x_size || !y_size) throw; }

   
Matrix(const inline int width, const inline int height) : //overload of 'Matrix' constructor for inline variables
        x_size
{width},
        y_size
{height}
   
{
       
if(!x_size || !y_size) //checking for parameters validity done at compile-time
           
static_assert(false, "Invalid width & height parameters.");
   
}

   
Matrix() = delete;
};


If we now instance 'Matrix' like this:

Matrix a(0, 0);

Our constructor overload with inline parameters will be executed, reporting compiler error.

And the second problem can be solved like:


struct B;

struct S {    

    B arr
[5];  
     
   
int & inline NumberOfSomething = arr[3].FieldName;
};

In which case in instances of 'B' inline reference 'NumberOfSomething' will be created with object name 'arr[3].FieldName', without allocating memory for it. Accessing it will be equal to accessing 'arr[3].FieldName'.

Thiago Macieira

unread,
Dec 19, 2014, 7:50:22 PM12/19/14
to std-pr...@isocpp.org
On Friday 19 December 2014 13:54:48 sasho648 wrote:
> And the second problem can be solved like:
>
>
> struct B;
>
> struct S {
>
> B arr[5];
>
> int & inline NumberOfSomething = arr[3].FieldName;
> };
>
> In which case in instances of 'B' inline reference 'NumberOfSomething' will
> be created with object name 'arr[3].FieldName', without allocating memory
> for it. Accessing it will be equal to accessing 'arr[3].FieldName'.

Did you mean for this variable to be "const inline"?

If it's not const, it stands to reason that the reference could be bound
elsewhere, for example in a copy constructor.

And besides, I don't think you can guarantee or require that it doesn't take
up space in the structure. Again the example of a copy constructor:

S s1;
assert(&s1.NumberOfSomething == &s1.arr[3].FieldName);

S s2 = s1;
assert(&s2.NumberOfSomething == &s1.arr[3].FieldName); // yes, s1

Since s2.NumberOfSomething was copied from s1.NumberOfSomething, it bound to
the same variable, which in turn means the compiler must keep space in the
struct for the reference since it can't know what it was bound to if the
object was constructed from a copy, not from the default constructor.

If you provide the copy and move constructors, then it still stands to reason
that NumberOfSomething can be bound to anything.

In short, I don't see how a member inline reference would be different from a
regular member reference.

sasho648

unread,
Dec 20, 2014, 9:43:56 AM12/20/14
to std-pr...@isocpp.org
It wouldn't work in the case because as I mentioned above, inline references will be bound either to another inline reference or to actual object name (not a reference to one of those). So such copy-constructor will be illegal (unless it's parameter is inline reference too):

struct S {
    S
(const S &arg) : NumberOfSomething (arg.NumberOfSomething) //error 'arg.NumberOfSomething' doesn't name object (but instead is a reference to such)

   
{ }

    B arr
[5];  
     
   
int & inline NumberOfSomething = arr[3].FieldName;
};

However I don't see a reason why would you want to copy such a reference as it's purpose is just to be another name of some data member. You could write the copy-constructor only for the data:

struct S {
    S
(const S &arg)
   
{ memcpy((void*)&arr, (void*)&arg.arr, sizeof(arr)); }


    B arr
[5];  
     
   
int & inline NumberOfSomething = arr[3].FieldName;
};

Also class members inlines can be only 'const' (inline references doesn't count as they're implicit constant).

Some examples of 'inline' references:

int *pa;

int a;

int & inline var_0 = (new S())->NumberOfSomething; //invalid '(new S())->NumberOfSomething' doesn't name an object

int & inline var_1 = *pa; //same as above

int & inline var_2 = a; //ok 'a' names an object

var_2
= 9; //same as 'a = 9'

I wanted to add that constructors which initialize 'inline' members, if their value can't be determined at the time of parsing the class which they belong to, and methods which use them are implicit inline and should be executed at compiler time. In such cases the class type is incomplete. In the above example this is not true because the value of 'NumberOfSomething' can be determined at class parsing time.

Douglas Boffey

unread,
Dec 20, 2014, 9:54:09 AM12/20/14
to std-pr...@isocpp.org

As I understand your suggestion, it comprises (please correct me if I have misunderstood anything):


  1. use the ‘inline’ keyword as part of the formal parameters of a function to specify that only constant expressions can be used,

  2. allow types for function parameters, and

  3. allow members that can reference other members without the need for extra storage, so as to permit different access for reading and writing the referenced member.


Inlined formal parameters

Should ISOCpp go down that road, wouldn’t it be more expressive to use ‘constexpr’ instead of ‘inline’, as that more accurately captures the intent.

It seems the only reason you want this syntax is for better compile-time checking. Whereas that is a worthy cause in itself, are you sure you’ve got the best solution?

As Thiago said:

I think the code above might be better written instead as:


template<typename T, size_t sz>

T &std::array<T,sz>::operator[](size_type pos)
{
        if (std::is_constexpr(pos))
                static_assert(pos < sz, "invalid index");

        // call internal, private function
        return this->_Internal_get(pos);
}

Where std::is_consteexpr [sic] is similar to the GCC builtin __builtin_constant_p,
but ensures that the static_assert won't be tried if the condition failed.

This would be a library solution (albeit necessitating core support), so is more likely to be accepted. I, personally, would far prefer a predicate along the lines of std::is_constexpr. I notice you haven’t explained what was wrong with this.


Allowing types for function parameters

Is the only reason for this your dislike of the current template syntax? I see from another thread that you find templates difficult. What do you find difficult with them?

I made the comment,

Firstly, whether or not you want this as a template replacement, I object to having to specify the type when the compiler can deduce that from the variables.

This was about type deduction, which can (easily) be done using the current template syntax, but you responded with examples where the compiler doesn’t do type deduction. Was I unclear in what I said?

You have come up with a novel syntax for expressing what is essentially a template specification:

auto FuncAdd(inline typename T == int, T a, T b) -> T



This is just confusing. You readily admitted that “You're absolutely right - we could create the 'int' overload of the function just like you said …” regarding my comment omitting the ‘inline typename T ==‘ from the formal parameter list, but haven’t explained why your solution is better (or even comparable).

As I said earlier (but without expanding on my reasons—sorry, but I was limited to a 120 character text):

It seems there is some confusion: a function template is a SET of
functions, so should have a different syntax.

p.s. I was not referring to template specialisations, but the fact that each separate type would necessitate a new function to be generated by the compiler.

Being a set, or family, of functions, we cannot take the address of it:

int foo(inline typename T, T v);

auto my_func = &foo; // illegal… cannnot point to more than one function at a time


However, I might need to take the address of one of the functions of the set, which seems impossible with this syntax. Using templates, we would write:

template <typename T> int foo1(T v);

auto my_func = &foo; // fine… decltype(my_func) is int (*)(T)


Class members referencing other members

As Thiago has stated, this will still need space within an object.

Other comments

It seems that you are right, and everyone else is wrong, which, unless your name is ‘God’, is the height of arrogance.

I can see that English is not you first language, but we still need to see better answers to our comments.

Please, please, please, don’t use foul language when we disagree with you. It is totally unprofessional and counter-productive.

Please don’t take these comments, or any other comments we have made on this thread wrongly. We do appreciate suggestions, just so long as you at least listen to what we are saying, and don’t assume you know better than any of us. We want to help.

You make it sound like ISOCpp are not concerned about proposals breaking existing code. You suggest that the introduction of a new keyword breaks code. While, in theory, new keywords do have the potential of breaking existing code, any new keywords (at least since C++98) are chosen carefully, so that the likelihood of that happening is remote.

constexpr is not useless, as you allege—it allows compile-time constants to invoke functions (along with other things). Prior to its introduction in C++11, for example, functions couldn’t be used in TMP (but ‘everyone doesn't care about meta-programming’).



sasho648

unread,
Dec 20, 2014, 2:44:39 PM12/20/14
to std-pr...@isocpp.org

@Inlined formal parameters



The library solution proposed is invalid because we must somehow specify in the declaration that the function is going to be executed at compile-time (otherwise instances to it can be replaced with just assembly calls or the function code itself). By using inline specifier we can do this, as I mentioned above, by specifiying either the return-value or parameters of function inline. Consider this:

void DoSomething(int a);

int main()
{
   
DoSomething(9); //here we don't know is 'DoSomething' going to be executed now or just being instanced for run-time execution
}

void DoSomething(int a)
{
   
if (std::is_constexpr(a))
       
static_assert(a < sz, "invalid index");
}

We could enable this library call only in 'constexpr' functions to avoid it.

But the real problem is different - why not allow proper compiler-constant distinction. What's the problem? Why we need to introduce 'constexpr' functions which may take compiler-known values but may not and then add explicit checks for that. Isn't this stupid and non-sense? Tell me please - I am not being offensive now but just - this is at-least not well-thought:

If his idea is approved here is what happens:

constexpr void Func(int a)
{
   
if (std::is_constexpr(a))
   
{
     
//Then work with 'a' as if it a constant expression

   
}
   
else
   
{
     
//Else work as if 'a' is not a constant expression

   
}
}

Imagine we have a multiple parameters now and the mess becomes full:

constexpr void Func(int a, int b, int c)
{
   
if (std::is_constexpr(a) && std::is_constexpr(b) && !std::is_constexpr(c))
   
{
     
//Then work as if 'a' and 'b' are constant expressions and 'c' is not

   
}
   
else if(std::is_constexpr(a) && !std::is_constexpr(b) && std::is_constexpr(c))
   
{
     
//Else work as if 'a' and 'c' are constant expressions and 'b' is not

   
}
   
else if(!std::is_constexpr(a) && std::is_constexpr(b) && std::is_constexpr(c))
   
{
     
//Else work as if 'b' and 'c' are constant expressions and 'a' is not

   
}


//....
}


@Allowing types for function parameters


This was about type deduction, which can (easily) be done using the current template syntax, but you responded with examples where the compiler doesn’t do type deduction. Was I unclear in what I said?

Sorry but I believe in the case you just have skipped this line, unknowingly of-course: 

"Automated 'type' deduction is being better implemented by using 'auto' keyword as function parameter or return-value. Like this:

auto FuncAdd(auto a, auto b) //also general function, which have parameters and return-value of any type 
{
    return a + b;
}
"

 ____________________________________

You have come up with a novel syntax for expressing what is essentially a template specification:
auto FuncAdd(inline typename T == int, T a, T b) -> T 


This is just confusing. You readily admitted that “You're absolutely right - we could create the 'int' overload of the function just like you said …” regarding my comment omitting the ‘inline typename T ==‘ from the formal parameter list, but haven’t explained why your solution is better (or even comparable).

 I admitted that and never said my solution is better. It was just an example, showing that similar functionality as templates can be achieved with my syntax. Quote again:


You're absolutely right - we could create the 'int' overload of the function just like you said but using templates is the same - you aren't required to specialize them but instead create non-template function with the needed type. I just wanted to show that the same behavior as 'template''s specializing is possible (but not required).

But now after you reminding about general function type with concrete inline variable values - it seemed that this syntax should be still required. However writing specialized functions won't be a good idea anyway.

Being a set, or family, of functions, we cannot take the address of it:
int foo(inline typename T, T v);
auto my_func = &foo; // illegal… cannnot point to more than one function at a time 

However, I might need to take the address of one of the functions of the set, which seems impossible with this syntax. Using templates, we would write:
template <typename T> int foo1(T v);
auto my_func = &foo; // fine… decltype(my_func) is int (*)(T) 

I believe in the second example you meant something like:

template <typename T> int foo1(T v);
auto my_func = &foo1<T>; // fine… decltype(my_func) is int (*)(T)

Because your example won't compile in the way you have written it. Anyway - yes we could do the same by taking an address of the function with specialized inline parameters or if a class, inline members. Example:

int foo1(inline class T, T v);

auto my_func = &(foo1(inline class == double, T) -> int); // fine.. decltype(my_func) is int (*)(inline class == double, T)

Here 'foo1(inline class == double, T) -> int' can be thought as complete function, just like, in your example, ' foo1<T>', with type 'int (*)(inline class == double, T)'. The syntax of this constructions is the following:

function-declaration -> return-type

As parameters which specialize the function will be selected with '=='.

Such functions will be instanced without  re-specifiying the specialized parameters. So 'my_func' will be instanced by only specifying 'v':

(*my_func)(9); //'v' argument = 9

For classes - the type of specialized object could be retrieved by getting the type of temporary object or using member specialization, similar way as above:

struct S
{
    S
(const inline class arg) : T(arg) { }

   
const inline class T;
};

The type of a specialized struct is written using the following syntax:

class-name { class-members }

As specializing class-members will be done via '=='. So the 'S' type with 'T' equal 'int' is:

S { T == int; }


Some examples

decltype(S(int)) S a{}; //will instance S::S(const inline == int), type received from constructing temporary

S b
(int); //same as above with type of 'S { T == int; }'

S
{ T == int; } c{}; //same as above


In this context I must admit that expressing it is harder than templates, so maybe they should be still a good choice for certain goals. However for others my construction will be prefered.

@Class members referencing other members


I wrote about this in my previous post.

@Other comments


Yep - sorry my English is very bad. I believe nobody care about meta-programming. When I suggested the ability to create enums by string someone said that he don't need this and can use script language to generate such things. Also some people think that not alerting user for compiler-time errors is something normal.

I'm not god but as I pointed out above some things are not-very sense.

sasho648

unread,
Dec 20, 2014, 4:00:32 PM12/20/14
to std-pr...@isocpp.org
Edit. I wanted to allow functions overloading no-matter that they have inline parameters or return-value that's way I insisted on adding the return-type also when instancing a specialization of it. Recently however I read that the return type can't be overloaded so specialized functions could be written just like a function declaration. So the example of 'my_func' can be now written like:

int foo1(inline class T, T v);

auto my_func = &foo1(inline class == double, double); // fine.. decltype(my_func) is int (*)(inline class == double, double)

Also specialized structures could be written only by specifying the inline members of them like this:

class-name ( = class-member-inline-value, = class-member-inline-value ...)

As each position will refer a sequenced inline member.

So the second example could be written like:

struct S
{
    S
(const inline class arg) : T(arg) { }

   
const inline class T;
};


decltype(S(int)) S a{}; //will instance S::S(const inline == int), type received from constructing temporary


S b
(int); //same as above with type of 'S(=int)'

S
(=int) c{}; //same as above

Thiago Macieira

unread,
Dec 20, 2014, 4:08:24 PM12/20/14
to std-pr...@isocpp.org
On Saturday 20 December 2014 11:44:39 sasho648 wrote:
> The library solution proposed is invalid because we must somehow specify in
> the declaration that the function is going to be executed at compile-time
> (otherwise instances to it can be replaced with just assembly calls or the
> function code itself).

I'm not sure you're aware of it, but we do have a keyword that tells the
compiler to try and execute a function at compile-time: constexpr.

Besides, the solution I presented and Douglas repeated allows for the compiler
to apply the check even if the whole function isn't run at compile-time, just
portions of it. So you get additional checks.

> By using inline specifier we can do this, as I
> mentioned above, by specifiying either the return-value or parameters of
> function inline. Consider this:

Why can you do it with a keyword A but not keyword B?

> void DoSomething(int a);
>
> int main()
> {
> DoSomething(9); //here we don't know is 'DoSomething' going to be
> executed now or just being instanced for run-time execution
> }
>
> void DoSomething(int a)
> {
> if (std::is_constexpr(a))
> static_assert(a < sz, "invalid index");
> }
>
> We could enable this library call only in 'constexpr' functions to avoid it.

Ok, but I don't think it's required to work. The above code could be left as-
is and the compiler could apply the check if it optimises that far down. It
would be depedent on the implementation whether it does the extra check or
not.

However, if you do mark the function or the argument as constexpr, then the
compiler is required to evaluate at compile-time.

The big difference to today is that a constexpr function, when called with
constexpr arguments, MUST evaluate wholly at compile time. It cannot branch off
to non-constexpr code. In other words, a constexpr function can't be used to
perform compile-time checks for a non-constexpr operation. To be honest, I
don't think it's a problem and none of your examples would require otherwise.

> But the real problem is different - why not allow proper compiler-constant
> distinction. What's the problem? Why we need to introduce 'constexpr'
> functions which may take compiler-known values but may not and then add
> explicit checks for that. Isn't this stupid and non-sense? Tell me please -
> I am not being offensive now but just - this is at-least not well-thought:

No, it's neither stupid nor nonsense.

Since a constexpr function may be evaluated at runtime too, we keep everything
in one place. It's harder to make mistakes if you don't have to keep two
copies in sync. If you need to enforce that the arguments are compile-time
only, you can static_assert(std::is_constexpr(...)), but you can't overload
that function.

> If his idea is approved here is what happens:
>
> constexpr void Func(int a)
> {
> if (std::is_constexpr(a))
> {
> //Then work with 'a' as if it a constant expression
>
> }
> else
> {
> //Else work as if 'a' is not a constant expression
>
> }
> }
>
> Imagine we have a multiple parameters now and the mess becomes full:
>
> constexpr void Func(int a, int b, int c)
> {
> if (std::is_constexpr(a) && std::is_constexpr(b) && !std::is_constexpr(c
> ))
> {
> //Then work as if 'a' and 'b' are constant expressions and 'c' is not
>
> }
> else if(std::is_constexpr(a) && !std::is_constexpr(b) && std::
> is_constexpr(c))
> {
> //Else work as if 'a' and 'c' are constant expressions and 'b' is not
>
> }
> else if(!std::is_constexpr(a) && std::is_constexpr(b) && std::
> is_constexpr(c))
> {
> //Else work as if 'b' and 'c' are constant expressions and 'a' is not
>
> }
>
>
> //....
> }

Why not? Maybe that's exactly what you want. I also see no difference between
the above and:

void Func(int a, int b, int c);
void Func(inline int a, int b, int c);
void Func(int a, inline int b, int c);
void Func(int a, int b, inline int c);
void Func(inline int a, inline int b, int c);
void Func(inline int a, int b, inline int c);
void Func(int a, inline int b, inline int c);
void Func(inline int a, inline int b, inline int c);

If you need all 8 combinations, then you need to write them out anyway
regardless of the syntax. However, with the solution based on "if", you can
write it all in a single function and merge the common code-paths without
having to call another function.

> "Automated 'type' deduction is being better implemented by using 'auto'
>
> > keyword as function parameter or return-value. Like this:
> >
> > auto FuncAdd(auto a, auto b) //also general function, which have
> > parameters and return-value of any type
> > {
> >
> > return a + b;
> >
> > }
> > "

The above is equivalent to:

template <typename X, typename Y>
auto FuncAdd(X a, Y b)
{
return a + b;
}

But there's no way to enforce that the types of a and b be the same. With
explicit templates, there is.

> > However, I might need to take the address of one of the functions of the
> > set, which seems impossible with this syntax. Using templates, we would
> > write:
> > template <typename T> int foo1(T v);
> > auto my_func = &foo; // fine… decltype(my_func) is int (*)(T)
>
> I believe in the second example you meant something like:
>
> template <typename T> int foo1(T v);
> auto my_func = &foo1<T>; // fine… decltype(my_func) is int (*)(T)
>
> Because your example won't compile in the way you have written it.

Correct, Douglas's example wouldn't compile, and in fact neither does yours.
To take the address of a function, you need a concrete specialisation.

auto my_func = &foo1<int>; // it's int (*)(int)
auto my_func2 = &foo1<double>; // it's int (*)(double)

// this is not a variable, it's a variable template:
template <typename X> auto my_func_template = &foo1<X>;

assert(my_func_template<int> == my_func);

> Anyway -
> yes we could do the same by taking an address of the function with
> specialized inline parameters or if a class, inline members. Example:
>
> int foo1(inline class T, T v);

Please explain why we need this syntax. What does it solve that the
traditional way of defining templates doesn't? And would "inline class" apply
to class templates too? How? If not, do you think it would be bad to have
different syntaxes for classes and for functions?

> auto my_func = &(foo1(inline class == double, T) -> int); // fine..
> decltype(my_func) is int (*)(inline class == double, T)

T isn't declared in this scope. I'm assuming you meant:

auto my_func = &(foo1(double, double)); // if this syntax were allowed
// the type is int (*)(double)

because, as you say:

> Such functions will be instanced without re-specifiying the specialized
> parameters. So 'my_func' will be instanced by only specifying 'v':
>
> (*my_func)(9); //'v' argument = 9


> For classes - the type of specialized object could be retrieved by getting
> the type of temporary object or using member specialization, similar way as
> above:
>
> struct S
> {
> S(const inline class arg) : T(arg) { }
>
> const inline class T;
> };

Did the above declare a member? It seems to me that it is a forward
declaration only. Maybe you meant a typedef instead, so it can be reused in
other functions.

How can I do a complete specialisation of such a class? For that matter, how
can I partially specialise a class with inline classes?

And again: why is this superior to the current way?

> The type of a specialized struct is written using the following syntax:
>
> *class-name **{ **class-members **}*
>
> As specializing class-members will be done via '=='. So the 'S' type with
> 'T' equal 'int' is:
>
> S { T == int; }

You're now overloading the braces to specialise a class. To initialise an
object, we'd have:

S{T = int}{0}; // I prefer = for assignment

What's more, you've made "T" part of the API of the class S. Right now, the
name that the templates assume inside a class are not part of the API. I can
select
S<int>{0}
without caring if it's T or _Tp or something else.

> Some examples
>
> decltype(S(int)) S a{}; //will instance S::S(const inline == int), type
> received from constructing temporary
>
> S b(int); //same as above with type of 'S { T == int; }'

This is a function declaration.

> S { T == int; } c{}; //same as above
>
> In this context I must admit that expressing it is harder than templates,
> so maybe they should be still a good choice for certain goals. However for
> others my construction will be prefered.

What other goals? If you want to convince us that the syntax is better, tell
us which cases it would be better for. You've so far only given examples that
either don't prove they're better or you admit they're worse.

> *@Class members referencing other members*
>
> I wrote about this in my previous post.

Fair enough. Since they're equivalent to an inline function returning a
reference anyway, we can drop this for now. We can discuss a "synthetic syntax
for inline function returning a reference" at a later time.

> *@Other comments*
>
> Yep - sorry my English is very bad. I believe nobody care about
> meta-programming.

You're wrong.

> When I suggested the ability to create enums by string
> someone said that he don't need this and can use script language to
> generate such things.

We do care about that, but that's a whole other area of discussion. You're
trying to cram everything and the kitchensink under one single feature. That
will never go past the committee.

Break it down into smaller features, explain why they're beneficial.

And for creating new types by way of functions, see past discussions on the
subject and participate in the reflection SG discussions.

> Also some people think that not alerting user for
> compiler-time errors is something normal.

That depends on the definition of compile-time error. All compile-time errors
should be reported. The discussion was whether the code was a compile-time
error in the first place or not.

I also prefer my code to be deterministic.

func(9); // will report an error

int x = 9;
func(x); // may report an error?

func(atoi(some_string)); // never reports an error?

Either the code is valid or it isn't. Anything that doesn't affect the validity
of the code should not be an error. It may be a warning.

Douglas Boffey

unread,
Dec 20, 2014, 6:41:50 PM12/20/14
to std-pr...@isocpp.org
Apologies for missing the <int> from my example, but I'm sure my
intention was obvious ;)

sasho648

unread,
Dec 21, 2014, 10:34:16 AM12/21/14
to std-pr...@isocpp.org
The above is equivalent to: 
template <typename X, typename Y> 
auto FuncAdd(X a, Y b) 

        return a + b; 

But there's no way to enforce that the types of a and b be the same. With 
explicit templates, there is. 

Wrong - there is a way that you can ensure that both of the parameters are from the same type, deduced from the first argument, by using 'decltype' and this actually compiles just fine in 'gcc 5.0':

auto FuncAdd(auto a, decltype(a) b) ;

Life example.

Please explain why we need this syntax. What does it solve that the 
traditional way of defining templates doesn't? And would "inline class" apply 
to class templates too? How? If not, do you think it would be bad to have 
different syntaxes for classes and for functions? 

It's not required but it for me it's naturally to be added as there is much similarity with 'constexpr' parameters and templates as they both are evaluated at compile-time. But if you want a reason - I personally think it's more easy to write 'inline' parameters, instead of templates. But for now let's abstract from it - I was just replaying 'Douglas Boffey' post. Let's continue with the concept of 'inline' or 'constexpr' parameters.

Why not? Maybe that's exactly what you want. I also see no difference between 
the above and: 
void Func(int a, int b, int c); 
void Func(inline int a, int b, int c); 
void Func(int a, inline int b, int c); 
void Func(int a, int b, inline int c); 
void Func(inline int a, inline int b, int c); 
void Func(inline int a, int b, inline int c); 
void Func(int a, inline int b, inline int c); 
void Func(inline int a, inline int b, inline int c); 
If you need all 8 combinations, then you need to write them out anyway 
regardless of the syntax. However, with the solution based on "if", you can 
write it all in a single function and merge the common code-paths without 
having to call another function. 

Why don't we stop using types and detect them in 'if' statements? That's the 'C++' language - it's a type-one.

Why can you do it with a keyword A but not keyword B? 

I don't have nothing against to be 'constexpr' too, after it is introduced in the language already and no going-back is possible. 

So as 'constexpr' functions could return only 'constexpr' values - we could thought them as an function declared with return value of type 'constexpr', if we replace 'inline' with 'constexpr'. And in the case we could declare 'constexpr' parameters.

That depends on the definition of compile-time error. All compile-time errors 
should be reported. The discussion was whether the code was a compile-time 
error in the first place or not. 
I also prefer my code to be deterministic. 
        func(9);                // will report an error 
        int x = 9; 
        func(x);                // may report an error? 
        func(atoi(some_string));        // never reports an error? 
Either the code is valid or it isn't. Anything that doesn't affect the validity 
of the code should not be an error. It may be a warning. 

I believe code which is sure 'UB' or instance functions with parameters not supported by them is exactly invalid code. 

sasho648

unread,
Dec 21, 2014, 10:45:07 AM12/21/14
to std-pr...@isocpp.org
The problem is best described here and solved using macros, and as it seems some of them are VC++ dependant, which is something that new 'C++' is trying to get rid of.

Thiago Macieira

unread,
Dec 21, 2014, 12:25:20 PM12/21/14
to std-pr...@isocpp.org
On Sunday 21 December 2014 07:34:16 sasho648 wrote:
> I don't have nothing against to be 'constexpr' too, after it is introduced
> in the language already and no going-back is possible.
>
> So as 'constexpr' functions could return only 'constexpr' values - we could
> thought them as an function declared with return value of type 'constexpr',
> if we replace 'inline' with 'constexpr'. And in the case we could declare
> 'constexpr' parameters.

Considering constexpr has been in the language for two revisions of the
standard already, does this paragraph mean you are ok with replacing "inline"
with "constexpr" in your proposal?

sasho648

unread,
Dec 21, 2014, 12:33:34 PM12/21/14
to std-pr...@isocpp.org
Does it matter what the keyword is? 

Thiago Macieira

unread,
Dec 21, 2014, 4:31:11 PM12/21/14
to std-pr...@isocpp.org
On Sunday 21 December 2014 09:33:34 sasho648 wrote:
> Does it matter what the keyword is?

Yes.

PS: please get a decent mail client that will set the proper In-Reply-To
header to the message you're replying to. All your messages on this thread
seem to come as replies to the original post.
Reply all
Reply to author
Forward
0 new messages