In the following code, a class owns a dynamically-allocated array. It exposes a read-only access to this array via a public method that does an implicit conversion to const.
#include <array>
template <class T, std::size_t N>
class A {
const unsigned int size;
std::array<T, N> *s;
public:
A(const unsigned int _size, const std::array<T, N>& def) :
size(_size),
s(new std::array<T, N>[size])
{
for (unsigned int i = 0; i < size; i++)
s[i] = def;
}
~A() { delete[] s; }
std::array<T, N> const* const conf() const { return s; }
};
int main()
{
A a(10, std::array<int, 3>{0, 0, 0});
auto x = a.conf();
return 0;
}
Does the implicit const conversion in A::conf() give rise to an overhead (for example by invoking a copy-constructor of the elements of A::s)?
Does the implicit const conversion in A::conf() give rise to an overhead (for example by invoking a copy-constructor of the elements of A::s)?
No.
There is no "overhead", a pointer can be implicitly converted in its const-version. Generally, the conversion generates zero assembly instruction.
Additional Notes
Empirical Proof
A::conf() does not call any copy-constructor. From the generated assembly (gcc -O3):
A<int, 3ul>::conf() const:
movq %rdi, %rax
ret
As you can see, it just returns the pointer without producing any further instructions.
Note: I disabled the "inlining optimization" here, otherwise the entire function would be optimized producing zero overhead.
Small Tips
Maybe a little bit off-topic, but it is good practice to avoid managing raw memory in modern C++ (whenever possible).
What about using std::vector?
In the signature std::array<T, N> const* const conf() const the second const is always ignored by the compiler. You can omit making it more readable: std::array<T, N> const* conf() const.
conf() returns a pointer (std::array<T, N> const*). Thus auto x = a.conf(); deduces the type of x to be a const pointer std::array<T, N> const*. The address/pointer is copied by value (typically copy 4byte/8byte address depending on your system). No c'tor is called.
Related
I'm writing a library which does numeric computations. I'm using templates so that the end-user can pick the precision they want. I would like this to work both with fundamental types (double, float) and high precision class types (e.g. boost::multiprecision). I'm wondering then if the argument types should be T or const & T.
On SO/google there are many posts about passing by value vs by reference. One of the "rules-of-thumb" seems to be:
Pass fundamental types by value
Pass everything else by const reference
However, this gets muddy if you have a template:
template<typename T>
T doSomething(T x, T y)
{
return x + y;
}
vs.
template<typename T>
T doSomething(const T & x, const T & y)
{
return x + y;
}
For boost::multiprecision you almost certainly want to pass by const reference. The question is whether passing double by const & is worse than by value. Many SO answers say const & is "no better and maybe worse"...but I couldn't find any good hard references.
I did the following benchmark
which seems to indicate there's no difference although maybe it depends on the simplicity of the function and inlining behavior.
There's the possibility of doing something like:
#include <type_traits>
template<typename T>
using choose_arg_type =
typename std::conditional<std::is_fundamental<T>::value,
T,
const T &>::type;
template <typename T>
T someFunc(choose_arg_type<T> arg)
{
return arg + arg;
}
int main()
{
auto result = someFunc<double>(0.0);
return 0;
}
But if it brings no benefit, it's added complexity and you lose type deduction (Any way to fix this type deduction?)
One reason I can think that pass by const reference is slower is, if it truly is using a reference, there may be cache locality issues. But if the compiler just optimizes to value...this won't matter.
What's the best way to handle this?
There is at least one circumstance where passing by const reference might disable optimizations. However, the most popular compilers provide a way to re-enable them.
Let’s look at this function:
int cryptographicHash( int& salt, const int& plaintext )
{
salt = 4; // Chosen by fair dice roll
// guaranteed to be random
return plaintext; // If we tell them there's a salt,
// this is the last hash function they'll
// ever suspect!
}
Looks pretty secure, right? But, since we’re writing in C++, is it as fast as it could possibly be? (Definitely what we want in a cryptographic hash.)
No, because what if you call it with:
int x = 0xFEED;
const int y = cryptographicHash( x, x );
Now the parameters passed by reference alias the same object, so the function should, as written, return 4, not 0xFEED. This means that, disastrously, the compiler can no longer optimize away the & in its const int& parameter.
However, the most popular compilers (including GCC, clang, Intel C++ and Visual C++ 2015 and up) all support the __restrict extension. So, change the function signature to int cryptographicHash( int& salt, const int& __restrict plaintext ) and all problems with it are solved forever.
Since this extension is not part of the C++ standard, you can improve portability with something like the following:
#if ( __GNUC__ || __clang__ || __INTEL_COMPILER || __ICL || _MSC_VER >= 1900 )
# define RESTRICT __restrict
#else
# define RESTRICT /**/
#endif
int cryptographicHash( int& salt, const int& RESTRICT plaintext );
(In GCC and clang, this does not appear to change the generated code.)
On platforms where the fundamental type in question fits into a register, a decent compiler should eliminate const references from parameters if it can see both sides of the call. For templates that is usually a given (unless they were explicitly instantiated somewhere). Since your library presumably has to be templated all the way down, this will apply to your case.
It's possible that your end users will have bad compilers or platforms where e.g. a double does not fit into a register. I don't see why you'd be incentivized to make micro-optimizations for these particular users, but maybe you do.
It's also possible that you want to explicitly instantiate all templates in your library for some set of types and provide implementation-less header files. In that case the user's compiler must obey whatever calling conventions exist on that platform and will probably pass fundamental types by reference.
Ultimately, the answer is "profile the relevant and representative use cases" if you don't have faith in the compiler(s).
Edit (removed macro solution): As suggested by Jarod42, the C++ way would be using an alias template. This also avoids the lack of deduction that the asker was running into with their original approach:
template<class T>
using CONSTREF = const T&; // Or just T for benchmarking.
https://godbolt.org/z/mopZ6B
As cppreference says:
Alias templates are never deduced by template argument deduction when deducing a template template parameter.
Passing something like int by reference (basically a pointer) is clearly sub-optimal since the extra indirection through the pointer can incur a cache miss and it may also prevent compiler optimizations since the compiler can't always know that the pointed-to variable cannot be changed by other entities, so it may in some cases be forced to do additional loads from memory. Passing by value removes the indirection and lets the compiler assume that noone else is changing the value.
This is a complex question that depends on architecture, compiler optimizations and many other specifics as the variation in answers shows. Since the OP is about writing template functions there is also the option of controlling which function is called by using SFINAE.
#include <iostream>
template <typename T, typename = typename std::enable_if_t<std::is_fundamental_v<T>> >
void f(T t) {
std::cout << "Pass by value\n";
}
template <typename T, typename = typename std::enable_if_t<not std::is_fundamental_v<T>> >
void f(T const &t) {
std::cout << "Pass by const ref.\n";
}
class myclass {};
int main() {
float x;
int i;
myclass c;
std::cout << "float: ";
f(x);
std::cout << "int: ";
f(i);
std::cout << "myclass: ";
f(c);
return 0;
}
Output:
float: Pass by value
int: Pass by value
myclass: Pass by const ref.
If an argument is trivially constructible and isn't modified, pass by value. The calling convention will automatically pass large structs by reference.
struct alignas(4096) page {unsigned char bytes[4096];};
[[nodiscard]] constexpr page operator^(page l, page r) noexcept {
for (int i = 0; i < 4096; ++i)
l.bytes[i] = l.bytes[i] ^ r.bytes[i];
return l;
}
Arguments that are modified and/or returned by nonconst reference must be passed by nonconst reference.
constexpr page& operator^=(page& l, page r) noexcept {return l = l ^ r;}
Pass any argument returned with const reference semantics by const reference.
using buffer = std::vector<unsigned char>;
[[nodiscard]] std::string_view to_string_view(const buffer& b) noexcept {
return {reinterpret_cast<const char*>(b.data()), b.size()};
}
Pass any argument deep copied to a different type by const reference.
[[nodiscard]] std::string to_string(const buffer& b) {
return std::string{to_string_view(b)};
}
Pass any non-trivially constructible, unmodified and non-deep copied argument by const reference.
std::ostream& operator<<(std::ostream& os, const buffer& b) {
os << std::hex;
for (const unsigned short u8 : b)
os << u8 << ',';
return os << std::dec;
}
Pass any argument deep copied to a value of the same type by value. There's no point in passing by reference an argument that's copied anyway, and the constructor for the returned copy is optimized away. See https://en.cppreference.com/w/cpp/language/copy_elision
[[nodiscard]] buffer operator^(buffer l, const buffer& r) {
const auto lsize = l.size();
const auto rsize = r.size();
const auto minsize = std::min(lsize, rsize);
for (buffer::size_type i = 0; i < minsize; ++i)
l[i] = l[i] ^ r[i];
if (lsize < rsize)
l.insert(l.end(), r.begin() + minsize, r.end());
return l;
}
This includes template functions as well.
template<typename T>
[[nodiscard]]
constexpr T clone(T t) noexcept(std::is_nothrow_constructible_v<T, T>) {
return t;
}
Otherwise take arguments of template parameter type by forwarding reference (&&). Note: && only has forwarding (universal) reference semantics in arguments of template parameter type, and/or for auto&& or decltype(auto)&&.
template<typename T>
constexpr bool nt = noexcept(std::is_nothrow_constructible_v<int, T&&>);
template<typename T>
[[nodiscard]]
constexpr int to_int(T&& t) noexcept(nt<T>) {return static_cast<int>(t);}
const auto to_int_lambda = [](auto&& t) noexcept(noexcept(to_int(t))) {
return to_int(t);
};
The problem seems simple enough yet I'm only able to solve it the "ugly" way. Here's a short code:
#include <array>
struct A {
A(int , int = 0) {}
A(std::array<const int, 2>) {}
//A(std::array<int, 2>) {}
};
int main(){
std::array<int, 2> a = {0};
const A x(a);
return 0;
}
As-is the compiler is trying to use the A(int, int = 0) constructor and, of course, fails the std::array to int conversion.
Commenting out the first constructor gives a clear compiler error that std::array<int, 2> cannot be automatically converted into it's const counterpart. And this is somewhat puzzling to me as I'd expect a non-const to const conversion to be "trivial".
The issue is resolved by introducing a third constructor (commented out in the code sample) but that looks like an overkill.
My questions are:
Why is the non-const to const conversion not done automatically here?
Can this be "fixed" without introducing the third, non-const version of the constructor?
Changing the constructor to accept gsl::span instead of std::array also helps, yet also feels like an overkill as well
I'm compiling on MSVC 2017 15.7.4 using the C++17 setting.
1) Why is the non-const to const conversion not done automatically here?
Because std::array<T, Dim> const and std::array<T const, Dim> are different types and, how say my clang++, "no known conversion from 'array<int, [...]>' to 'array<const int, [...]>'"
2) Can this be "fixed" without introducing the third, non-const version of the constructor?
What about a template constructor
template <typename T>
A (std::array<T, 2> const &) {}
where T can match both int and int const ?
If you want impose that T is only int or int const (and not, by example, long const) you can do it through SFINAE with something as
template <typename T>
A (std::array<T, 2>,
std::enable_if_t<std::is_same<T const, int const>{}> * = nullptr)
{ }
So you can have
std::array<int, 2> a = {{0}};
std::array<int const, 2> b = {{0}};
std::array<long const, 2> c = {{0}};
const A x(a); // compile
const A y(b); // compile
const A z(c); // compilation error
3) Changing the constructor to accept gsl::span instead of std::array also helps, yet also feels like an overkill as well
Sorry but I don't understand the third question (?) (and I don't know gls::span)
I have a class that represents a value, and can assume either the value of a single number, single string, an array of values, or a map of key value pairs.
Here is the currrent definition:
class Foo {
public:
typedef enum { STRING, NUMBER, ARRAY, MAP } data_type;
struct str_less {
bool operator()(const char *a, const char *b) const {
return strcmp(a,b)<0;
}
};
inline Foo(int n):type(NUMBER),number_value(n) { }
inline Foo(double n):type(NUMBER),number_value(n) { }
inline Foo(const char *s):type(STRING),string_value(s) { }
inline Foo(const std::initializer_list<std::pair<const char *const,Foo>> &arg):type(MAP),map_value(arg) { }
template<size_t N> inline Foo(const Foo (&arg)[N]):type(ARRAY) { std::copy(&arg[0], &arg[N], std::back_inserter(arg)); }
inline Foo(const std::vector<Foo> &arg):type(ARRAY),array_value(arg) { }
private:
data_type type;
double number_value = 0;
const char *string_value = "";
std::vector<Foo> array_value;
std::map<const char *,Foo, str_less> map_value;
};
I am only ever interested in instantiating this class from values given at compile time... for my purposes, it would never be called at run-time with variables as arguments.
Because of the constructors in Foo, any literal values specified at compile time would get automatically type-converted into a Foo, and I could then specify literals in an almost json-like manner such as:
Foo({
{"number_key", 100},
{"array_key", std::vector<Foo>
{1, 3, 4,
{
{"inner_key", "value"},
{"second_key", 500}}, "abc"}}});
The caveat, however, as you can see, is that I appear to require an explicit cast to an std::vector to support array types. Is there any way in C++11 to pass a literal array of some type to a function, so that I can call the template<size_t N> Foo::Foo(const Foo (&arg)[N]) appropriately? I would greatly prefer this, because requiring the explicit vector cast feels awkward, and most definitely not homogeneous with the rest of the constructors that automatically convert their types. I am wondering if there is any other way that can utilize smart automatic type conversion to do it in a more uniform and concise way?
I have seen code like this before:
template<std::size_t N> void do_stuff(const char (&str)[N]) ...
Which can be called with string literals, so conceptually the mechanism appears to exist for understanding compile-time literal arrays, but is there any way to specify a constant array of types other than char?
Something like this should work:
template <typename... Args>
Foo(Args&&... args) :
array_value{Foo(std::forward<Args>(args))...}
{}
Foo foo(4.2, "hello");
Proof of concept
I am trying to make a compile-time string class. I took a few hints from this post. Unfortunately, I'm stuck on constructor overload precedence: the const char[] constructor is being ignored in favor of the const char* constructor. Any tips would be appreciated!
class string {
public:
// Can be done compile time. Works lovely! except...
template<size_t N>
constexpr string(const char(&char_array)[N])
: ptr_(char_array), length_(N-1) {}
// This override gets called instead. I *must* keep this constructor.
string(const char* const str)
: ptr_(str) {
length_ = strlen(str);
}
// Ugly hack. (not acceptable)
template<size_t N>
constexpr string(const char(&char_array)[N], double unused)
: ptr_(char_array), length_(N-1) {}
private:
const char* ptr_;
int length_;
};
constexpr const char kConstant[] = "FooBarBaz";
constexpr string kString(kConstant); // Error: constexpr variable 'kString' must be initialized by a constant expression (tries to call wrong overload)
constexpr string kString(kConstant, 1.0f); // ugly hack works.
There's lots of cool things I can do if I can make compile-time string constants.
string equality testing is faster on string than const char *
Eliminate run-time overhead of implicit conversions from const char * to string that call strlen() on compile-time constant strings.
Compile-time string sets that do equality testing instead of hashing for size < N. (this is multiple cpus of overhead on one application I'm looking at)
This is a bit ugly, but it should work:
template<class T, class = std::enable_if_t<std::is_same_v<T, char>>>
string(const T * const & str)
: ptr_(str) {
length_ = strlen(str);
}
The trick is that taking the pointer by const reference blocks array-to-pointer decay during template argument deduction, so when you pass an array, the compiler can't deduce T and the constructor is ignored.
The drawback is that this would also reject other things that are implicitly convertible to const char *.
An alternative might be to accept everything convertible to const char *, and then dispatch based on whether said thing is an array.
template<size_t N>
constexpr string(const char(&char_array)[N], std::true_type)
: ptr_(char_array), length_(N-1) {}
string(const char * str, std::false_type)
: ptr_(str) {
length_ = strlen(str);
}
template<class T, class = std::enable_if_t<std::is_convertible_v<T, const char *>>>
constexpr string(T&& t)
: string(std::forward<T>(t), std::is_array<std::remove_reference_t<T>>()) {}
Using C++11, Ubuntu 14.04, GCC default toolchain.
This code fails:
constexpr std::string constString = "constString";
error: the type ‘const string {aka const std::basic_string}’ of
constexpr variable ‘constString’ is not literal... because...
‘std::basic_string’ has a non-trivial destructor
Is it possible to use std::string in aconstexpr? (apparently not...) If so, how? Is there an alternative way to use a character string in a constexpr?
As of C++20, yes, but only if the std::string is destroyed by the end of constant evaluation. So while your example will still not compile, something like this will:
constexpr std::size_t n = std::string("hello, world").size();
However, as of C++17, you can use string_view:
constexpr std::string_view sv = "hello, world";
A string_view is a string-like object that acts as an immutable, non-owning reference to any sequence of char objects.
No, and your compiler already gave you a comprehensive explanation.
But you could do this:
constexpr char constString[] = "constString";
At runtime, this can be used to construct a std::string when needed.
C++20 will add constexpr strings and vectors
The following proposal has been accepted apparently: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0980r0.pdf and it adds constructors such as:
// 20.3.2.2, construct/copy/destroy
constexpr
basic_string() noexcept(noexcept(Allocator())) : basic_string(Allocator()) { }
constexpr
explicit basic_string(const Allocator& a) noexcept;
constexpr
basic_string(const basic_string& str);
constexpr
basic_string(basic_string&& str) noexcept;
in addition to constexpr versions of all / most methods.
There is no support as of GCC 9.1.0, the following fails to compile:
#include <string>
int main() {
constexpr std::string s("abc");
}
with:
g++-9 -std=c++2a main.cpp
with error:
error: the type ‘const string’ {aka ‘const std::__cxx11::basic_string<char>’} of ‘constexpr’ variable ‘s’ is not literal
std::vector discussed at: Cannot create constexpr std::vector
Tested in Ubuntu 19.04.
Since the problem is the non-trivial destructor so if the destructor is removed from the std::string, it's possible to define a constexpr instance of that type. Like this
struct constexpr_str {
char const* str;
std::size_t size;
// can only construct from a char[] literal
template <std::size_t N>
constexpr constexpr_str(char const (&s)[N])
: str(s)
, size(N - 1) // not count the trailing nul
{}
};
int main()
{
constexpr constexpr_str s("constString");
// its .size is a constexpr
std::array<int, s.size> a;
return 0;
}
C++20 is a step toward making it possible to use std::string at compile time, but P0980 will not allow you to write code like in your question:
constexpr std::string constString = "constString";
the reason is that constexpr std::string is allowed only to be used in constexpr function (constant expression evaluation context). Memory allocated by constexpr std::string must be freed before such function returns - this is the so called transient allocation, and this memory cannot 'leak' outside to runtime to constexpr objects (stored in data segments) accessible at runtime . For example compilation of above line of code in current VS2022 preview (cl version : 19.30.30704) results in following error:
1> : error C2131: expression did not evaluate to a constant
1> : message : (sub-)object points to memory which was heap allocated during constant evaluation
this is because it tries to make a non-transient allocation which is not allowed - this would mean allocation into a data segment of the compiled binary.
In p0784r1, in "Non-transient allocation" paragraph, you can find that there is a plan to allow conversion of transient into static memory (emphasis mine):
What about storage that hasn't been deallocated by the time evaluation
completes? We could just disallow that, but there are really
compelling use cases where this might be desirable. E.g., this could
be the basis for a more flexible kind of "string literal" class. We
therefore propose that if a non-transient constexpr allocation is
valid (to be described next), the allocated objects are promoted to
static storage duration.
There is a way to export transient std::string data outside to make it usable at runtime. You must copy it to std::array, the problem is to compute the final size of std::array, you can either preset some large size or compute std::string twice - once to get size and then to get atual data. Following code successfully compiles and runs on current VS2022 preview 5. It basicly joins three words with a delimiter between words:
constexpr auto join_length(const std::vector<std::string>& vec, char delimiter) {
std::size_t length = std::accumulate(vec.begin(), vec.end(), 0,
[](std::size_t sum, const std::string& s) {
return sum + s.size();
});
return length + vec.size();
}
template<size_t N>
constexpr std::array<char, N+1> join_to_array(const std::vector<std::string>& vec, char delimiter) {
std::string result = std::accumulate(std::next(vec.begin()), vec.end(),
vec[0],
[&delimiter](const std::string& a, const std::string& b) {
return a + delimiter + b;
});
std::array<char, N+1> arr = {};
int i = 0;
for (auto c : result) {
arr[i++] = c;
}
return arr;
}
constexpr std::vector<std::string> getWords() {
return { "one", "two", "three" };
}
int main()
{
constexpr auto arr2 = join_to_array<join_length(getWords(), ';')>(getWords(), ';');
static_assert(std::string(&arr2[0]) == "one;two;three");
std::cout << &arr2[0] << "\n";
}