I'm writing a constrexpr function taking either a CArray T(&)(N), either a std::array.
I think I have to write 2 functions (if you know better I would be happy to know),
But I'm concerned about what I wrote with the std::array
constexpr float LinInterp01(const std::array<float, N>& inArray, float inX);
Is it correct when writing a constrexpr function to pass by const & or not?
I think it should be because at compile time the compiler would instanciate a copy and there is no notion of L Value, at compile time.
Could someone explain me this?
C++ standard section § 7.1.5 [dcl.constexpr]
The definition of a constexpr function shall satisfy the following constraints:
— it shall not be virtual (10.3);
— its return type shall be a literal type;
— each of its parameter types shall be a literal type;
And section § 3.9 [basic.types]
A type is a literal type if it is:
— void; or
— a scalar type; or
— a reference type; or
— an array of literal type; or
— a class type (Clause 9) that has all of the following properties:
— it has a trivial destructor,
— it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template
that is not a copy or move constructor, and
— all of its non-static data members and base classes are of non-volatile literal types.
So yes, you can pass parameters by reference to constexpr functions.
Now whether or not your function calls will actually be evaluated at compile time depends on the body and calls of LinInterp01.
Related
In C++11, std::enable_if was added to the Standard Library. It is equivalent to boost::enable_if_c whose condition is a bool. This is suitable for rather simple conditions, but as soon as you use predicates that hold their result in a value constant, you have to use the more verbose construct my_predicate<MyArgs>::value to turn it into bool.
This is exactly what boost::enable_if (without _c suffix) was made for.
Why is there no equivalent in Standard Library?
The standard library goes a different route here. C++17 added variable templates shortcuts for all the type traits that return a ::value. The pattern is always
template <typename... Args>
some_trait_v = some_trait<Args...>::value;
For instance you can write
std::enable_if<std::is_same_v<T1,T2>>
Further the argument for enable_if could be the result of constexpr expressions, for instance
std::enable_if<some_constexpr_function<T1,T2>()>
This way is more generic and does not depend on passing something that must have a value member.
uint32_t u32 = 0;
uint16_t u16[2];
static_assert(sizeof(u32) == sizeof(u16), "");
memcpy(u16, &u32, sizeof(u32)); // defined?
// if defined, how to we access the data from here on?
Is this defined behaviour? And, if so, what type of pointer may we use to access the target data after the memcpy?
Must we use uint16_t*, because that suitable for the declared type of u16?
Or must we use uint32_t*, because the type of the source data (the source data copied from by memcpy) is uint_32?
(Personally interested in C++11/C++14. But a discussion of related languages like C would be interesting also.)
Is this defined behavio[u]r?
Yes. memcpying into a pod is well-defined and you ensured that the sizing is the correct.
Must we use uint16_t*, because that suitable for the declared type of u16?
Yes, of course. u16 is an array of two uint16_ts so it must be accessed as such. Accessing it via a uint32_t* would be undefined behavior by the strict-aliasing rule.
It doesn't matter what the source type was. What matters is that you have an object of type uint16_t[2].
On the other hand, this:
uint32_t p;
new (&p) uint16_t(42);
std::cout << p;
is undefined behavior, because now there is an object of a different type whose lifetime has begin at &p and we're accessing it through the wrong type.
The C++ standard delegates to C standard:
The contents and meaning of the header <cstring> are the same as the C standard library header <string.h>.
The C standard specifies:
7.24.1/3 For all functions in this subclause, each character shall be interpreted as if it had the type unsigned char (and therefore every possible object representation is valid and has a different value).
So, to answer your question: Yes, the behaviour is defined.
Yes, uint16_t* is appropriate because uint16_t is the type of the object.
No, the type of the source doesn't matter.
C++ standard doesn't specify such thing as object without declared type or how it would behave. I interpret that to mean that the effective type is implementation defined for objects with no declared type.
Even in C, the source doesn't matter in this case. A more complete version of quote from C standard (draft, N1570) that you are concerned about, emphasis mine:
6.5/6 [...] If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one. [...]
This rule doesn't apply, because objects in u16 do have a declared type
The following code compiles cleanly with GCC 5.2.1
enum class some_enum
{
first = static_cast<some_enum>(1),
};
but fails in clang 3.6.2:
$ clang++ -std=c++11 enum.cpp -c
enum.cpp:15:13: error: value of type 'some_enum' is not implicitly convertible to 'int'
first = static_cast<some_enum>(1),
^~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
According to my reading of the standard (§7.2, paragraph 5), the enumerator initializer expression must have the same type as the underlying type of the enumeration in this case. Which happens to be int, not some_enum.
So I think clang is correct to reject this, and that this is a bug in GCC. Can anyone with a better understanding of the standard confirm, before I report this as a bug to GCC?
Edit: to explain my reasoning regarding the standard
Here is my understanding of 7.2 paragraph 5:
Each enumeration defines a type that is different from all other
types. Each enumeration also has an underlying type. The underlying
type can be explicitly specified using enum-base; if not explicitly
specified, the underlying type of a scoped enumeration type is int. In
these cases, the underlying type is said to be fixed.
So, since this a scoped enumeration with no explicit enum-base, its underlying type is fixed as int.
Following the closing brace of an enum-specifier, each enumerator
has the type of its enumeration.
In other words, outside the enum body, an individual enumerator (i.e., named entry within the enum) is has the same type as the entire enum.
If the underlying type is fixed, the type of each enumerator prior to
the closing brace is the underlying type...
Since we're dealing with a scoped enum which always has fixed underlying type, the type of each enum entry within the enum body has the same type as the enumeration's underlying type. In this case, within the body, the enumerators have type int.
...and the constant-expression in the enumerator-definition shall
be a converted constant expression of the underlying type (5.19);
if the initializing value of an enumerator cannot be represented
by the underlying type, the program is ill-formed.
In the example case, the initializer has type some_enum, which as a C++11 scoped enum cannot be converted to a constant expression of the underlying type without an explicit conversion. So the program is ill-formed.
In C++11 emplace_back() is generally preferred (in terms of efficiency) to push_back() as it allows in-place construction, but is this still the case when using push_back(std::move()) with an already-constructed object?
For instance, is emplace_back() still preferred in cases like the following?
std::string mystring("hello world");
std::vector<std::string> myvector;
myvector.emplace_back(mystring);
myvector.push_back(std::move(mystring));
// (of course assuming we don't care about using the value of mystring after)
Additionally, is there any benefit in the above example to instead doing:
myvector.emplace_back(std::move(mystring));
or is the move here entirely redundant, or has no effect?
Let's see what the different calls that you provided do:
emplace_back(mystring): This is an in-place construction of the new element with whatever argument you provided. Since you provided an lvalue, that in-place construction in fact is a copy-construction, i.e. this is the same as calling push_back(mystring)
push_back(std::move(mystring)): This calls the move-insertion, which in the case of std::string is an in-place move-construction.
emplace_back(std::move(mystring)): This is again an in-place construction with the arguments you provided. Since that argument is an rvalue, it calls the move-constructor of std::string, i.e. it is an in-place move-construction like in 2.
In other words, if called with one argument of type T, be it an rvalue or lvalue, emplace_back and push_back are equivalent.
However, for any other argument(s), emplace_back wins the race, for example with a char const* in a vector<string>:
emplace_back("foo") calls std::string(char const*) for in-place-construction.
push_back("foo") first has to call std::string(char const*) for the implicit conversion needed to match the function's signature, and then a move-insertion like case 2. above. Therefore it is equivalent to push_back(string("foo"))
The emplace_back gets a list of rvalue references and tries to construct a container element direct in place. You can call emplace_back with all types which the container element constructors supports.
When call emplace_back for parameters which are not rvalue references, it 'falls back' to normal references and at least the copy constructor ist called when the parameter and the container elements are of the same type.
In your case 'myvector.emplace_back(mystring)' should make a copy of the string becaus the compiler could not know that the parameter myvector is movable. So insert the std::move what gives you the desired benefit.
The push_back should work as well as emplace_back for already constructed elements.
From C++ Draft Standard N3337:
7.1.6.2 Simple type specifiers
4 The type denoted by decltype(e) is defined as follows:
— if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;
— otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;
— otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
If I understand the above correctly,
int a;
decltype(a) b = 10; // type of b is int
int* ap;
decltype(*ap) c = 10; // Does not work since type of c is int&
Can you explain, or point to some documentation that explains, why decltype(*ap) couldn't have been just int?
The standardization effort of decltype was a herculean effort spanning many years. There were 7 versions of this paper before the committee finally accepted it. The versions were:
N1478 2003-04-28
N1527 2003-09-21
N1607 2004-02-17
N1705 2004-09-12
N1978 2006-04-24
N2115 2006-11-05
N2343 2007-07-18
Remarkably, the seeds of the behavior which you question are in the very first revision: N1478, which introduces the need for "two types of typeof: either to preserve or to drop references in types."
This paper goes on to give a rationale for the reference-preserving variant, including this quote:
On the other hand, the reference-dropping semantics fails to provide a
mechanism for exactly expressing the return types of generic
functions, as demonstrated by Strous- trup [Str02]. This implies that
a reference-dropping typeof would cause problems for writers of
generic libraries.
There is no substitute for reading through these papers. However one might summarize that decltype serves two purposes:
To report the declared type of an identifier.
To report the type of an expression.
For the second use case, recall that expressions are never reference types, but are instead one of lvalues, xvalues, or prvalues. By convention, when decltype is reporting the type of an lvalue expression, it makes the type an lvalue reference, and when the expression is an xvalue, the reported type becomes an rvalue reference.
In your example, *ap is an expression, whereas a is an identifier. So your example makes use of both use cases, as first introduced in N1478.
It is also instructive to note that decltype was not designed in isolation. The rest of the C++ language was evolving during this time period (e.g. rvalue references), and the design of decltype was iterated to keep pace.
Also note that once the decltype proposal was accepted, it continued (and continues to this day) to evolve. See this list of issues:
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_index.html
specifically section 7.1.6.2 (which is the section where the bulk of the decltype specification lives).