array<int, 5> arr;
cout << arr[98] << endl; //compiles just fineconstexpr int EvaluateSpecialArrayIndex(int a)
{ return a * sizeof(int); }
cout << arr[EvaluateSpecialArrayIndex(4)] << endl;int a = rand() % 4; //lvalue
cout << arr[EvaluateSpecialArrayIndex(a)] << endl; // also validconstexpr int EvaluateSpecialArrayIndex(int a)
{ static_assert( a < 5, "invalid a" ); return a * sizeof(int); }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'
}array<int, 5> arr;
cout << arr[98] << endl;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); }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' variablesclass A
{
int _a;
public:
const int &a = _a;
} ;class A
{
int _a;
public:
constexpr int &a = _a; // the same as 'const int & constexpr a' - still produces error
} ;class A
{
int _a;
public:
const int & inline a = _a; // inline reference to '_a'
} ;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;
}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')Did you forget the inline before size_type above?
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'
}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.
template<bool bIsInline, typename T = (bIsInline ? double : int)>
auto SomeAlgo(T n) -> decltype(n) 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
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'
}
int (inline Func)();
auto Func() -> inline int;int constexpr = 67 * 8;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()
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.
int (inline Func)()
auto Func() -> inline intconstexpr Func()"very bad"? How much code have you ever found that did that?
Well, I guess the "bad" and "not-well implemented" parts are debatable.
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.
using FuncReturnType = inline int;
FuncReturnType Func();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
...
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.
auto FuncAdd(auto a, auto b) //also general function, which have parameters and return-value of any type
{
return a + b;
}double a, b;
FuncAdd(int, a, b); //here 'a' and 'b' will be implicitly converted into 'int'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.
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;
}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
}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
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;
}enum EN_AUTO_CREATED = CreateEnumAscendingNumber("MEMBER_", 10);
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.
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.
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.
template<typename T, size_t sz>
void SomeFunc()
{
T tmpArr[sz];
} void SomeFunc(inline typename T, inline size_t sz)
{
T tmpArr[sz];
}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];
}
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 abovetemplate<typename T>
void Func(T a);
Func(9);void Func(auto a);
Func(9); //'a' type is deduced from the passed argumentOn 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,
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
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++.
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;
};
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
array<int, 5> arr;
cout << arr[98] << endl; //compiles without errorstruct B;
struct S {
B arr[5];
int &NumberOfSomething = arr[3].FieldName;
};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;
};Matrix a(0, 0);struct B;
struct S {
B arr[5];
int & inline NumberOfSomething = arr[3].FieldName;
};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;
};struct S {
S(const S &arg)
{ memcpy((void*)&arr, (void*)&arg.arr, sizeof(arr)); }
B arr[5];
int & inline NumberOfSomething = arr[3].FieldName;
};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'As I understand your suggestion, it comprises (please correct me if I have misunderstood anything):
use the ‘inline’ keyword as part of the formal parameters of a function to specify that only constant expressions can be used,
allow types for function parameters, and
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.
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.
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)
As Thiago has stated, this will still need space within an object.
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’).
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");
}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
}
}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
}
//....
}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?
"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).
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).
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)
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)(*my_func)(9); //'v' argument = 9struct S
{
S(const inline class arg) : T(arg) { }
const inline class T;
};S { T == int; }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
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)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 aboveThe 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.
auto FuncAdd(auto a, decltype(a) b) ;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?
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 can you do it with a keyword A but not keyword B?
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.