I am trying to build a constructor to take an array as an argument which overloads another who take a scalar instead. Code is below.
#include <iostream>
template <typename T>
class SmallVec { // This is a 3 dimensional vector class template
public:
T data[3] = {0}; // internal data of class
template <typename U>
explicit SmallVec(const U& scalar) { // if a scalar, copy it to each element in data
for(auto &item : data) {
item = static_cast<T>(scalar);
}
}
template <typename U>
explicit SmallVec(const U* vec) { // if a vector, copy one by one
for(auto &item : data) {
item = static_cast<T>(*vec);
vec++;
}
}
};
int main() {
float num = 1.2;
float *arr = new float[3];
arr[2] = 3.4;
SmallVec<float> vec1(num); // take num, which works fine
SmallVec<float> vec2(arr); // !!!--- error happens this line ---!!!
std::cout << vec1.data[2] << " " << vec2.data[2] << std::endl;
return 0;
}
The compiler complains that
error: invalid static_cast from type 'float* const' to type 'float'
Obviously, vec2(arr) still calls the first constructor. However, if I remove template <typename U> and replace U to T. The program just works fine. What should I do to correct this?
Any suggestions are appreciated!
Here's how to use SFINAE to get what you want:
#include <vector>
#include <map>
#include <string>
using namespace std;
template<class T>
struct Foo {
template <class U, typename enable_if<is_pointer<U>::value, int>::type = 0>
Foo(U u){}
template <class U, typename enable_if<!is_pointer<U>::value, int>::type = 0>
Foo(U u){}
};
int main()
{
Foo<int> f('a'); // calls second constructor
Foo<int> f2("a"); // calls first constructor
}
live: https://godbolt.org/g/ZPcb5T
I am trying to build a constructor to take an array as an argument
(...)
explicit SmallVec(const U* vec) { // if a vector, copy one by one
You do not take an array. You take a pointer, which may or may not point to an array, and even if it points to an array, who says that the array has at least three elements? That's a serious design flaw.
C++ does allow you to take raw arrays by reference or const reference, even though the syntax is horrible:
explicit SmallVec(const U (&vec)[3]) {
The implementation of the constructor is then also different:
for(int index = 0; index < 3; ++index) {
data[index] = static_cast<T>(vec[index]);
}
Looking at main, however, the problem goes deeper. You use new[] to allocate an array dynamically. That's already a very bad idea. Coincidentally, your example also misses a delete[]. Why don't you use a local array instead?
float arr[3];
This will make your program compile and probably run correctly, but there's still undefined behaviour in your code, because you only set the 3rd element of the array to a valid value; the other two elements remain uninitialised, and reading from an uninitialised float, even if you just copy it, formally results in undefined behaviour.
So better make it:
float arr[3] = { 0.0, 0.0, 3.4 };
In addition to that, C++11 invites you to use std::array, which generally makes things a bit safer and improves the syntax. Here is a complete example:
#include <iostream>
#include <array>
template <typename T>
class SmallVec { // This is a 3 dimensional vector class template
public:
std::array<T, 3> data; // internal data of class
template <typename U>
explicit SmallVec(const U& scalar) { // if a scalar, copy it to each element in data
for(auto &item : data) {
item = static_cast<T>(scalar);
}
}
template <typename U>
explicit SmallVec(std::array<U, 3> const& vec) { // if a vector, copy one by one
for(int index = 0; index < 3; ++index) {
data[index] = static_cast<T>(vec[index]);
}
}
};
int main() {
float num = 1.2;
std::array<float, 3> arr = { 0.0, 0.0, 3.4 };
SmallVec<float> vec1(num);
SmallVec<float> vec2(arr);
std::cout << vec1.data[2] << " " << vec2.data[2] << std::endl;
return 0;
}
Even though both constructors use the explicit specifier and try to avoid type conversions you should note that the first is just as good a candidate as the second. If you substitute U for float* you will get:
explicit SmallVec(const float*& scalar)
which is totally acceptable and will explain the compilation error.
You could resolve the problem by changing the second constructor to:
template <typename U>
explicit SmallVec(U* const vec) { // if a vector, copy one by one
U* local = vec;
for(auto &item : data) {
item = static_cast<T>(*local);
local++;
}
}
However, I suggest an even more explicit way:
class ScalarCopy {};
class VectorCopy {};
...
template <typename U>
SmallVec(const U& vec, ScalarCopy);
template <typename U>
SmallVec(const U* const vec, VectorCopy);
and make explicit calls:
SmallVec<float> vec1(num, ScalarCopy());
SmallVec<float> vec2(arr, VectorCopy());
Related
I want to write a function that returns a preallocated vector.
Since it should work for any kind of vector, I am using a template.
My question is whether it is possible to deduce the template parameter of the function call with the template parameter of the left hand side vector ?
template<typename T>
std::vector<T> createVector(size_t initial) {
std::vector<T> vec;
vec.reserve(initial);
return vec;
}
struct Foo {
};
int main() {
//std::vector<Foo> foos = createVector(4); // this should work
//auto foos = createVector<Foo>(4); // this should be okay, too
std::vector<Foo> foos = createVector<Foo>(4); //duplicate Type :(
}
Is it possible to deduce the template parameter of the function call with the template parameter of the left hand side vector ?
No, except for conversion operator (for classes):
struct CreateVector
{
std::size_t m_capacity;
CreateVector(std::size_t capacity) : m_capacity(capacity) {}
template <typename T>
operator std::vector<T>() const
{
std::vector<T> vec;
vec.reserve(m_capacity);
return vec;
}
};
and then
struct Foo {};
int main() {
[[maybe_unused]]std::vector<Foo> foos = CreateVector(4);
}
Demo
I have a templated class which has its own storage, but can also be used to 'view' (or even modify) a bigger array using a pointer to some position in the array. Viewing the bigger array is done using e.g. (see full example below):
Tensor<double> t;
t.view(&array[i]);
When the array is marked const the following can be used
Tensor<const double> t;
t.view(&array[i]);
My problem is now:
I want to write a function with ONE template argument for Tensor<...>, that can be used to map the const-array and to modify a copy of the map. How can I remove the const from a 'nested template'? Or, if that is not possible, how can I use the map without marking it const in the template?
Note that I have currently no conversion from Tensor<const double> to Tensor<double>.
A simple, complete, example showing this behavior is
#include <iostream>
#include <vector>
template<class T>
class Tensor
{
private:
T m_container[4];
T *m_data;
public:
// constructor
Tensor() { m_data = &m_container[0];};
// copy constructor
Tensor(const Tensor& other)
{
for ( auto i = 0 ; i < 4 ; ++i )
m_container[i] = other[i];
m_data = &m_container[0];
}
// index operator
T& operator[](size_t i) { return m_data[i]; }
const T& operator[](size_t i) const { return m_data[i]; }
// point to external object
void view(T *data) { m_data = data; }
};
template<class T>
T someOperation(std::vector<double> &input, size_t i)
{
T t;
t.view(&input[i*4]);
// ... some code that uses "t" but does not modify it
T s = t;
return s;
}
int main()
{
std::vector<double> matrix = { 1., 2., 3., 4., 11., 12., 13., 14. };
Tensor<double> tensor = someOperation<Tensor<double>>(matrix, 1);
return 0;
}
Compiled for example using clang++ -std=c++14 so.cpp.
Now I want to change the signature of the function to
template<class T>
T someOperation(const std::vector<double> &input, size_t i)
The above function can be used using
someOperation<Tensor<const double>>(...)
But obviously I cannot change s anymore. How can I solve this?
Consider std::remove_const:
template<typename T>
void f(T& t);
double const d = 1.0;
f(d);
template<typename T>
void f(T& t)
{
T ct = t;
//ct += 1.0; // refuses to compile!
typename std::remove_const<T>::type nct = t;
nct += 1.0; // fine...
}
Edit: OK, only half of the truth...
With your example provided, matter gets a little more complicated, as you need to exchange the inner template type...
This can be done with a template template function:
template<template < class > class T, typename V>
auto someOperation(std::vector<double>& input, size_t i)
{
T<V> t;
t.view(&input[i*4]);
T<typename std::remove_const<V>::type> s = t;
return s;
}
However, this imposes quite some trouble on you:
Constant members cannot be initialized in the constructor body, so you need:Tensor(Tensor const& other)
: m_container { other[0], other[1], other[2], other[3] },
m_data(m_container)
{ }
Tensor<double> and Tensor<double const> are entirely different types, so they need to be constructible one from another:Tensor(Tensor<typename std::remove_const<T>::type> const& other);
Tensor(Tensor <T const> const& other);
// both with same implementation as above
We don't need all combinations, but we get them for free... Alternatively, a template constructor:template<typename TT>
Tensor(Tensor<TT> const& other);This would even allow you to initialize e. g. a double tensor from e. g. an int tensor – if desired or not, decide you...
Am I allowed to move elements out of a std::initializer_list<T>?
#include <initializer_list>
#include <utility>
template<typename T>
void foo(std::initializer_list<T> list)
{
for (auto it = list.begin(); it != list.end(); ++it)
{
bar(std::move(*it)); // kosher?
}
}
Since std::intializer_list<T> requires special compiler attention and does not have value semantics like normal containers of the C++ standard library, I'd rather be safe than sorry and ask.
No, that won't work as intended; you will still get copies. I'm pretty surprised by this, as I'd thought that initializer_list existed to keep an array of temporaries until they were move'd.
begin and end for initializer_list return const T *, so the result of move in your code is T const && — an immutable rvalue reference. Such an expression can't meaningfully be moved from. It will bind to an function parameter of type T const & because rvalues do bind to const lvalue references, and you will still see copy semantics.
Probably the reason for this is so the compiler can elect to make the initializer_list a statically-initialized constant, but it seems it would be cleaner to make its type initializer_list or const initializer_list at the compiler's discretion, so the user doesn't know whether to expect a const or mutable result from begin and end. But that's just my gut feeling, probably there's a good reason I'm wrong.
Update: I've written an ISO proposal for initializer_list support of move-only types. It's only a first draft, and it's not implemented anywhere yet, but you can see it for more analysis of the problem.
bar(std::move(*it)); // kosher?
Not in the way that you intend. You cannot move a const object. And std::initializer_list only provides const access to its elements. So the type of it is const T *.
Your attempt to call std::move(*it) will only result in an l-value. IE: a copy.
std::initializer_list references static memory. That's what the class is for. You cannot move from static memory, because movement implies changing it. You can only copy from it.
This won't work as stated, because list.begin() has type const T *, and there is no way you can move from a constant object. The language designers probably made that so in order to allow initializer lists to contain for instance string constants, from which it would be inappropriate to move.
However, if you are in a situation where you know that the initializer list contains rvalue expressions (or you want to force the user to write those) then there is a trick that will make it work (I was inspired by the answer by Sumant for this, but the solution is way simpler than that one). You need the elements stored in the initialiser list to be not T values, but values that encapsulate T&&. Then even if those values themselves are const qualified, they can still retrieve a modifiable rvalue.
template<typename T>
class rref_capture
{
T* ptr;
public:
rref_capture(T&& x) : ptr(&x) {}
operator T&& () const { return std::move(*ptr); } // restitute rvalue ref
};
Now instead of declaring an initializer_list<T> argument, you declare aninitializer_list<rref_capture<T> > argument. Here is a concrete example, involving a vector of std::unique_ptr<int> smart pointers, for which only move semantics is defined (so these objects themselves can never be stored in an initializer list); yet the initializer list below compiles without problem.
#include <memory>
#include <initializer_list>
class uptr_vec
{
typedef std::unique_ptr<int> uptr; // move only type
std::vector<uptr> data;
public:
uptr_vec(uptr_vec&& v) : data(std::move(v.data)) {}
uptr_vec(std::initializer_list<rref_capture<uptr> > l)
: data(l.begin(),l.end())
{}
uptr_vec& operator=(const uptr_vec&) = delete;
int operator[] (size_t index) const { return *data[index]; }
};
int main()
{
std::unique_ptr<int> a(new int(3)), b(new int(1)),c(new int(4));
uptr_vec v { std::move(a), std::move(b), std::move(c) };
std::cout << v[0] << "," << v[1] << "," << v[2] << std::endl;
}
One question does need an answer: if the elements of the initializer list should be true prvalues (in the example they are xvalues), does the language ensure that the lifetime of the corresponding temporaries extends to the point where they are used? Frankly, I don't think the relevant section 8.5 of the standard addresses this issue at all. However, reading 1.9:10, it would seem that the relevant full-expression in all cases encompasses the use of the initializer list, so I think there is no danger of dangling rvalue references.
I thought it might be instructive to offer a reasonable starting point for a workaround.
Comments inline.
#include <memory>
#include <vector>
#include <array>
#include <type_traits>
#include <algorithm>
#include <iterator>
template<class Array> struct maker;
// a maker which makes a std::vector
template<class T, class A>
struct maker<std::vector<T, A>>
{
using result_type = std::vector<T, A>;
template<class...Ts>
auto operator()(Ts&&...ts) const -> result_type
{
result_type result;
result.reserve(sizeof...(Ts));
using expand = int[];
void(expand {
0,
(result.push_back(std::forward<Ts>(ts)),0)...
});
return result;
}
};
// a maker which makes std::array
template<class T, std::size_t N>
struct maker<std::array<T, N>>
{
using result_type = std::array<T, N>;
template<class...Ts>
auto operator()(Ts&&...ts) const
{
return result_type { std::forward<Ts>(ts)... };
}
};
//
// delegation function which selects the correct maker
//
template<class Array, class...Ts>
auto make(Ts&&...ts)
{
auto m = maker<Array>();
return m(std::forward<Ts>(ts)...);
}
// vectors and arrays of non-copyable types
using vt = std::vector<std::unique_ptr<int>>;
using at = std::array<std::unique_ptr<int>,2>;
int main(){
// build an array, using make<> for consistency
auto a = make<at>(std::make_unique<int>(10), std::make_unique<int>(20));
// build a vector, using make<> because an initializer_list requires a copyable type
auto v = make<vt>(std::make_unique<int>(10), std::make_unique<int>(20));
}
Instead of using a std::initializer_list<T>, you can declare your argument as an array rvalue reference:
template <typename T>
void bar(T &&value);
template <typename T, size_t N>
void foo(T (&&list)[N] ) {
std::for_each(std::make_move_iterator(std::begin(list)),
std::make_move_iterator(std::end(list)),
&bar);
}
void baz() {
foo({std::make_unique<int>(0), std::make_unique<int>(1)});
}
See example using std::unique_ptr<int>: https://gcc.godbolt.org/z/2uNxv6
It seems not allowed in the current standard as already answered. Here is another workaround to achieve something similar, by defining the function as variadic instead of taking an initializer list.
#include <vector>
#include <utility>
// begin helper functions
template <typename T>
void add_to_vector(std::vector<T>* vec) {}
template <typename T, typename... Args>
void add_to_vector(std::vector<T>* vec, T&& car, Args&&... cdr) {
vec->push_back(std::forward<T>(car));
add_to_vector(vec, std::forward<Args>(cdr)...);
}
template <typename T, typename... Args>
std::vector<T> make_vector(Args&&... args) {
std::vector<T> result;
add_to_vector(&result, std::forward<Args>(args)...);
return result;
}
// end helper functions
struct S {
S(int) {}
S(S&&) {}
};
void bar(S&& s) {}
template <typename T, typename... Args>
void foo(Args&&... args) {
std::vector<T> args_vec = make_vector<T>(std::forward<Args>(args)...);
for (auto& arg : args_vec) {
bar(std::move(arg));
}
}
int main() {
foo<S>(S(1), S(2), S(3));
return 0;
}
Variadic templates can handle r-value references appropriately, unlike initializer_list.
In this example code, I used a set of small helper functions to convert the variadic arguments into a vector, to make it similar to the original code. But of course you can write a recursive function with variadic templates directly instead.
I have a much simpler implementation that makes use of a wrapper class which acts as a tag to mark the intention of moving the elements. This is a compile-time cost.
The wrapper class is designed to be used in the way std::move is used, just replace std::move with move_wrapper, but this requires C++17. For older specs, you can use an additional builder method.
You'll need to write builder methods/constructors that accept wrapper classes inside initializer_list and move the elements accordingly.
If you need some elements to be copied instead of being moved, construct a copy before passing it to initializer_list.
The code should be self-documented.
#include <iostream>
#include <vector>
#include <initializer_list>
using namespace std;
template <typename T>
struct move_wrapper {
T && t;
move_wrapper(T && t) : t(move(t)) { // since it's just a wrapper for rvalues
}
explicit move_wrapper(T & t) : t(move(t)) { // acts as std::move
}
};
struct Foo {
int x;
Foo(int x) : x(x) {
cout << "Foo(" << x << ")\n";
}
Foo(Foo const & other) : x(other.x) {
cout << "copy Foo(" << x << ")\n";
}
Foo(Foo && other) : x(other.x) {
cout << "move Foo(" << x << ")\n";
}
};
template <typename T>
struct Vec {
vector<T> v;
Vec(initializer_list<T> il) : v(il) {
}
Vec(initializer_list<move_wrapper<T>> il) {
v.reserve(il.size());
for (move_wrapper<T> const & w : il) {
v.emplace_back(move(w.t));
}
}
};
int main() {
Foo x{1}; // Foo(1)
Foo y{2}; // Foo(2)
Vec<Foo> v{Foo{3}, move_wrapper(x), Foo{y}}; // I want y to be copied
// Foo(3)
// copy Foo(2)
// move Foo(3)
// move Foo(1)
// move Foo(2)
}
This is one of cases where const_cast is good to use
Sum::Sum(std::initializer_list<Valuable>&& l)
{
for (auto& a : l)
{
auto&& arg = std::move(const_cast<Valuable&>(a));
Add(std::move(arg));
}
}
Consider the in<T> idiom described on cpptruths. The idea is to determine lvalue/rvalue at run-time and then call move or copy-construction. in<T> will detect rvalue/lvalue even though the standard interface provided by initializer_list is const reference.
Am I allowed to move elements out of a std::initializer_list<T>?
#include <initializer_list>
#include <utility>
template<typename T>
void foo(std::initializer_list<T> list)
{
for (auto it = list.begin(); it != list.end(); ++it)
{
bar(std::move(*it)); // kosher?
}
}
Since std::intializer_list<T> requires special compiler attention and does not have value semantics like normal containers of the C++ standard library, I'd rather be safe than sorry and ask.
No, that won't work as intended; you will still get copies. I'm pretty surprised by this, as I'd thought that initializer_list existed to keep an array of temporaries until they were move'd.
begin and end for initializer_list return const T *, so the result of move in your code is T const && — an immutable rvalue reference. Such an expression can't meaningfully be moved from. It will bind to an function parameter of type T const & because rvalues do bind to const lvalue references, and you will still see copy semantics.
Probably the reason for this is so the compiler can elect to make the initializer_list a statically-initialized constant, but it seems it would be cleaner to make its type initializer_list or const initializer_list at the compiler's discretion, so the user doesn't know whether to expect a const or mutable result from begin and end. But that's just my gut feeling, probably there's a good reason I'm wrong.
Update: I've written an ISO proposal for initializer_list support of move-only types. It's only a first draft, and it's not implemented anywhere yet, but you can see it for more analysis of the problem.
bar(std::move(*it)); // kosher?
Not in the way that you intend. You cannot move a const object. And std::initializer_list only provides const access to its elements. So the type of it is const T *.
Your attempt to call std::move(*it) will only result in an l-value. IE: a copy.
std::initializer_list references static memory. That's what the class is for. You cannot move from static memory, because movement implies changing it. You can only copy from it.
This won't work as stated, because list.begin() has type const T *, and there is no way you can move from a constant object. The language designers probably made that so in order to allow initializer lists to contain for instance string constants, from which it would be inappropriate to move.
However, if you are in a situation where you know that the initializer list contains rvalue expressions (or you want to force the user to write those) then there is a trick that will make it work (I was inspired by the answer by Sumant for this, but the solution is way simpler than that one). You need the elements stored in the initialiser list to be not T values, but values that encapsulate T&&. Then even if those values themselves are const qualified, they can still retrieve a modifiable rvalue.
template<typename T>
class rref_capture
{
T* ptr;
public:
rref_capture(T&& x) : ptr(&x) {}
operator T&& () const { return std::move(*ptr); } // restitute rvalue ref
};
Now instead of declaring an initializer_list<T> argument, you declare aninitializer_list<rref_capture<T> > argument. Here is a concrete example, involving a vector of std::unique_ptr<int> smart pointers, for which only move semantics is defined (so these objects themselves can never be stored in an initializer list); yet the initializer list below compiles without problem.
#include <memory>
#include <initializer_list>
class uptr_vec
{
typedef std::unique_ptr<int> uptr; // move only type
std::vector<uptr> data;
public:
uptr_vec(uptr_vec&& v) : data(std::move(v.data)) {}
uptr_vec(std::initializer_list<rref_capture<uptr> > l)
: data(l.begin(),l.end())
{}
uptr_vec& operator=(const uptr_vec&) = delete;
int operator[] (size_t index) const { return *data[index]; }
};
int main()
{
std::unique_ptr<int> a(new int(3)), b(new int(1)),c(new int(4));
uptr_vec v { std::move(a), std::move(b), std::move(c) };
std::cout << v[0] << "," << v[1] << "," << v[2] << std::endl;
}
One question does need an answer: if the elements of the initializer list should be true prvalues (in the example they are xvalues), does the language ensure that the lifetime of the corresponding temporaries extends to the point where they are used? Frankly, I don't think the relevant section 8.5 of the standard addresses this issue at all. However, reading 1.9:10, it would seem that the relevant full-expression in all cases encompasses the use of the initializer list, so I think there is no danger of dangling rvalue references.
I thought it might be instructive to offer a reasonable starting point for a workaround.
Comments inline.
#include <memory>
#include <vector>
#include <array>
#include <type_traits>
#include <algorithm>
#include <iterator>
template<class Array> struct maker;
// a maker which makes a std::vector
template<class T, class A>
struct maker<std::vector<T, A>>
{
using result_type = std::vector<T, A>;
template<class...Ts>
auto operator()(Ts&&...ts) const -> result_type
{
result_type result;
result.reserve(sizeof...(Ts));
using expand = int[];
void(expand {
0,
(result.push_back(std::forward<Ts>(ts)),0)...
});
return result;
}
};
// a maker which makes std::array
template<class T, std::size_t N>
struct maker<std::array<T, N>>
{
using result_type = std::array<T, N>;
template<class...Ts>
auto operator()(Ts&&...ts) const
{
return result_type { std::forward<Ts>(ts)... };
}
};
//
// delegation function which selects the correct maker
//
template<class Array, class...Ts>
auto make(Ts&&...ts)
{
auto m = maker<Array>();
return m(std::forward<Ts>(ts)...);
}
// vectors and arrays of non-copyable types
using vt = std::vector<std::unique_ptr<int>>;
using at = std::array<std::unique_ptr<int>,2>;
int main(){
// build an array, using make<> for consistency
auto a = make<at>(std::make_unique<int>(10), std::make_unique<int>(20));
// build a vector, using make<> because an initializer_list requires a copyable type
auto v = make<vt>(std::make_unique<int>(10), std::make_unique<int>(20));
}
Instead of using a std::initializer_list<T>, you can declare your argument as an array rvalue reference:
template <typename T>
void bar(T &&value);
template <typename T, size_t N>
void foo(T (&&list)[N] ) {
std::for_each(std::make_move_iterator(std::begin(list)),
std::make_move_iterator(std::end(list)),
&bar);
}
void baz() {
foo({std::make_unique<int>(0), std::make_unique<int>(1)});
}
See example using std::unique_ptr<int>: https://gcc.godbolt.org/z/2uNxv6
It seems not allowed in the current standard as already answered. Here is another workaround to achieve something similar, by defining the function as variadic instead of taking an initializer list.
#include <vector>
#include <utility>
// begin helper functions
template <typename T>
void add_to_vector(std::vector<T>* vec) {}
template <typename T, typename... Args>
void add_to_vector(std::vector<T>* vec, T&& car, Args&&... cdr) {
vec->push_back(std::forward<T>(car));
add_to_vector(vec, std::forward<Args>(cdr)...);
}
template <typename T, typename... Args>
std::vector<T> make_vector(Args&&... args) {
std::vector<T> result;
add_to_vector(&result, std::forward<Args>(args)...);
return result;
}
// end helper functions
struct S {
S(int) {}
S(S&&) {}
};
void bar(S&& s) {}
template <typename T, typename... Args>
void foo(Args&&... args) {
std::vector<T> args_vec = make_vector<T>(std::forward<Args>(args)...);
for (auto& arg : args_vec) {
bar(std::move(arg));
}
}
int main() {
foo<S>(S(1), S(2), S(3));
return 0;
}
Variadic templates can handle r-value references appropriately, unlike initializer_list.
In this example code, I used a set of small helper functions to convert the variadic arguments into a vector, to make it similar to the original code. But of course you can write a recursive function with variadic templates directly instead.
I have a much simpler implementation that makes use of a wrapper class which acts as a tag to mark the intention of moving the elements. This is a compile-time cost.
The wrapper class is designed to be used in the way std::move is used, just replace std::move with move_wrapper, but this requires C++17. For older specs, you can use an additional builder method.
You'll need to write builder methods/constructors that accept wrapper classes inside initializer_list and move the elements accordingly.
If you need some elements to be copied instead of being moved, construct a copy before passing it to initializer_list.
The code should be self-documented.
#include <iostream>
#include <vector>
#include <initializer_list>
using namespace std;
template <typename T>
struct move_wrapper {
T && t;
move_wrapper(T && t) : t(move(t)) { // since it's just a wrapper for rvalues
}
explicit move_wrapper(T & t) : t(move(t)) { // acts as std::move
}
};
struct Foo {
int x;
Foo(int x) : x(x) {
cout << "Foo(" << x << ")\n";
}
Foo(Foo const & other) : x(other.x) {
cout << "copy Foo(" << x << ")\n";
}
Foo(Foo && other) : x(other.x) {
cout << "move Foo(" << x << ")\n";
}
};
template <typename T>
struct Vec {
vector<T> v;
Vec(initializer_list<T> il) : v(il) {
}
Vec(initializer_list<move_wrapper<T>> il) {
v.reserve(il.size());
for (move_wrapper<T> const & w : il) {
v.emplace_back(move(w.t));
}
}
};
int main() {
Foo x{1}; // Foo(1)
Foo y{2}; // Foo(2)
Vec<Foo> v{Foo{3}, move_wrapper(x), Foo{y}}; // I want y to be copied
// Foo(3)
// copy Foo(2)
// move Foo(3)
// move Foo(1)
// move Foo(2)
}
This is one of cases where const_cast is good to use
Sum::Sum(std::initializer_list<Valuable>&& l)
{
for (auto& a : l)
{
auto&& arg = std::move(const_cast<Valuable&>(a));
Add(std::move(arg));
}
}
Consider the in<T> idiom described on cpptruths. The idea is to determine lvalue/rvalue at run-time and then call move or copy-construction. in<T> will detect rvalue/lvalue even though the standard interface provided by initializer_list is const reference.
This may be a bizarre question, but I have a recursive template that expects an array of size d (where d is a template parameter), and I want to pass the d-1 template the same array as if it was one element shorter. This way I only work with one array, instead of creating a new one for each level.
I feel like the answer may be something very basic, but I can't come up with any search terms that result in anything close to what I'm looking for.
To put this into context, here's an example
template<int d>
void Function(int array[d])
{
array[d- 1]= d;
Function<d- 1>(?);
}
This answer is for static, C-style arrays, If your question is about std::Array, I apologize.
Off the top of my head, I came up with two ways to do the recursion, but many more techniques exist.
The first one uses a partially specialized class (with array count of zero) to terminate the recursion.
The second way uses a cast to a statically-chosen type which ends the recursion with an overloaded function. Here, I cast the array to void*, but for types that won't work with this, you could create a custom type which is constructible from the original type.
I resorted to using reinterpret_cast to change the array's type from a reference to array[count] to array[count-1]. Although I expect this to be safe as it is used here, keep in mind that you might run into problems in different situations.
#include <iostream>
// Ends recursion with a partial specialization
template <typename T, int count>
struct StaticArrayDump {
static void func(T(&a)[count]) {
using shorter_t = T(&)[count-1];
StaticArrayDump<T, count-1>::func(reinterpret_cast<shorter_t>(a));
std::cout << a[count-1] << ' ';
}
};
template <typename T>
struct StaticArrayDump<T,0> {
static void func(...) {}
};
template <typename T, int count>
static void static_array_dump_spec(T(&a)[count]) {
using shorter_t = T(&)[count-1];
StaticArrayDump<T,count>::func(a);
}
// Ends recursion with void* cast and function overload
// Ultimately relies on type_select's specialization, however
template <bool, typename A, typename B> struct type_select /* true */ { using type = A; };
template <typename A, typename B> struct type_select<false,A,B> { using type = B; };
template <bool cond, typename A, typename B>
using type_select_t = typename type_select<cond, A, B>::type;
static void static_array_dump_ovld(...) {}
template <typename T, int count>
static void static_array_dump_ovld(T(&a)[count]) {
static const int next_count = count-1;
using shorter_t = T(&)[next_count];
static_array_dump_ovld(reinterpret_cast<
type_select_t<next_count!=0, shorter_t, void*>
>(a));
// output the last element
std::cout << a[count-1] << ' ';
}
// This is an overload-based version which is free of
// any reliance on template specialization.
// helper_trueol's (void*, void*) overload will only be
// selected for arguments (array_ref, count) when count
// is 0, because 0 is the only integer which can be
// converted to a pointer.
// This one's compiler compatibility is a bit shaky...
// MSVC 2013 OK
// IdeOne g++ needs int cast for next_count
static void helper_trueol(void*, void*) {}
template <typename T, int count>
static void helper_trueol(T(&a)[count], int) {
static const int next_count = count-1;
using shorter_t = T(&)[next_count];
helper_trueol(reinterpret_cast<shorter_t>(a), int(next_count));
std::cout << a[count-1] << ' ';
}
template <typename T, int count>
static void static_array_dump_trueol(T(&a)[count]) {
helper_trueol(a, count);
}
// Finally, this overload-based version relies
// on SFINAE to disqualify the template function
// as a candidate when count is 0 because the
// zero-length array type triggeres a substitution
// failure.
// So just using this template array argument type,
// the same one used in all of the previous examples,
// but without any extra mechanisms, is all you need
// to end this recursion!
// This is the obvious best way, of course.
static void static_array_dump_sfinae(...) {}
template <typename T, int count>
static void static_array_dump_sfinae(T(&a)[count]) {
static const int next_count = count-1;
using shorter_t = T(&)[next_count];
static_array_dump_sfinae(reinterpret_cast<shorter_t>(a));
std::cout << a[count-1] << ' ';
}
//////
int main() {
double dbl_array[] = { 0, 1.2, 3.4, 5.6789, 10 };
static_array_dump_spec(dbl_array);
std::cout << '\n';
const char* cstr_array[] = { "zero", "one", "two", "three", "four" };
static_array_dump_ovld(cstr_array);
std::cout << '\n';
char charray[] = "Hello";
charray[sizeof(charray)-1] = '!'; // replace nul terminator
static_array_dump_trueol(charray);
std::cout << '\n';
bool barray[] = {true, true, true, false, true, false, false, false};
std::cout << std::boolalpha;
static_array_dump_sfinae(barray);
std::cout << '\n';
}
Hopefully I am interpreting this correctly but, when you pass the array as a template, I assume you pass an argument for the size of the array. (otherwise how would you know how large the array is?) When you pass the array, you are passing a pointer to the first element of the array, so when you pass the sub array you could just pass a pointer to the next element and size d-1 or an iterator that points to the next element and size d-1.
Example:
template< typename T>
T foo(T * ptr, int size) {
if (size > 0)
return *ptr + foo(ptr + sizeof(T), size - 1);
else
return 0;
}
From the information that you provided i assume you want too know how too "stop" the recursion. This would look something like this:
// this is the function that will be called from the user, it would be bad design too have to pass an integral constant manually when we can easily do this
template <std::size_t I>
inline
void Function(int (&_array)[I])
{
Function(_array, std::integral_constant<std::size_t, I>);
}
// function will recursively do something with an array for each of it's elements
template<std::size_t I>
void Function(int (&_array)[I], std::integral_constant<std::size_t, I>)
{
// do something...
Function(_array,std::integral_constant<std::size_t,I-1>);
}
// function as before with a few modifications
template<std::size_t I>
void Function(int (&_array)[I], std::integral_constant<std::size_t, 1>)
{
// do something...
// exit function...
}