Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed last month.
Improve this question
Consider a BasicCharp class with two constructors:
template <class CharType>
class BasicCharp {
public:
using value_type = CharType;
using pointer = value_type*;
public:
//1
template<size_t SIZE>
constexpr BasicCharp(value_type (&str)[SIZE]) noexcept
: m_Length(SIZE)
, m_String(str) {}
//2
constexpr BasicCharp(pointer string) noexcept
: m_Length(countElems(string))
, m_String(string) {}
private:
size_t m_Length;
pointer m_String;
};
Constructing:
BasicCharp<const char> str = "test";
This calls constructor 2, however if there is no constructor 2, this does call constructor 1.
How to keep constructor 2, but enforce using constructor 1 if possible?
A c++17 solution is preferred, but any standart is welcome.
The goal is to deduce string literal's size at compile time, but if a pointer is passed, size should be calculated by the constructor.
Your overloads are ambiguous; a decayed array pointer is no better or worse candidate than a non-decayed one. It only compiles because non-template overloads are a better match than template ones.
What you can do is make the pointer overload a worse overload candidate, e.g. by introducing a user-defined conversion sequence.
struct ptr { pointer p; ptr(pointer p) : p(p) {} };
constexpr BasicCharp(ptr string) noexcept
: m_Length(countElems(string.p))
, m_String(string.p) {}
Not exactly the same, but you might tag dispatch, something like:
template <typename > struct Tag{};
template <class CharType>
class BasicCharp {
// ...
public:
template<typename T>
constexpr BasicCharp(const T& str) noexcept : BasicCharp(str, Tag<T>{})
{
// Or alternatively, use if constexpr here
}
private:
template<std::size_t SIZE>
constexpr BasicCharp(const value_type (&str)[SIZE],
Tag<value_type[SIZE]>) noexcept
: m_Length(SIZE)
, m_String(str) {}
constexpr BasicCharp(const value_type*string, Tag<const value_type*>) noexcept
: m_Length(countElems(string))
, m_String(string) {}
};
You can assign the result to constexpr variable, but it is not 100% garanted that this will be done compile time, also you can use consteval, in this case if variable isn't assigned compile time you will get error(consteval works from c++20)
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 months ago.
Improve this question
With this nice simplified compile time string class.
There are no constexpr/consteval function parameters in C++
but the point of this question is:
Can I get a function call style for passing of non-type template parameters ?
With the suffix I can get pretty close.
But can it be done better ?
I do not want to use this syntax
test_normal_function2<"asd">(x);
How can I get rid of _fs suffix in the invocation of test_normal_function ?
#include <algorithm>
struct no_init_fixed_string {};
template <std::size_t N>
struct fixed_string
{
constexpr fixed_string(const char (&foo)[N + 1])
{
std::copy_n(foo, N + 1, data.begin());
}
std::array<char, N + 1> data{};
constexpr fixed_string(no_init_fixed_string)
: data{}
{
}
};
template <std::size_t N>
fixed_string(const char (&str)[N]) -> fixed_string<N - 1>;
template<fixed_string s>
struct fixed_string_type
{
};
template<fixed_string s>
constexpr auto operator"" _fs()
{
return fixed_string_type<s>{};
}
template<fixed_string s>
void test_normal_function(fixed_string_type<s>, int x )
{
//use s as consexpr parameter
}
template<fixed_string s>
void test_normal_function2( int x )
{
}
int main()
{
int x = 3;
test_normal_function("asd"_fs,x); //this works
test_normal_function2<"asd">(x); // this works but its ugly
//test_normal_function("asd",x); //this doesn't compile but its pretty.
}
How can I make call to test_normal_function work without _fs suffix ?
Preprocessor macro trickery aside, it is impossible to do it with the exact syntax you want, because a usual string literal (not a user-defined literal) does never encode its value in its type (only its length).
A value passed to a function via a function parameter can never be used by the function in a constant expression. Only information in the type can be used that way.
However, you can pass a value via a template parameter and make use of it in a constant expression:
test_normal_function<"asd">({},x);
I know that trying to use a std::initializer_list<NonCopyable> leads to an error because the elements are copied into the temporary array represented by the initializer_list. I have also read some explanation on why it would not be alright to have rvalue references in the list, which I'm fine with.
The problem is that I would like to pass noncopyable things not in order to move from them, but only const-access them, so the argument about rvalues does not apply. What can I do to retain, if possible, the list initialization syntax and the reference semantics (no wrappers, no raw pointers)?
NonCopyable a{...}, b{...};
ListInitialized c{a, b};
I think I'm missing something extremely obvious here.
Update:
This works(*),
ListInitialized(std::initializer_list<std::reference_wrapper<NonCopyable>>) {...}
but won't accept rvalues. It would be nice if I could simply pass a list of anything that could go into const NonCopyable&.
(*) I know I wrote "no wrappers" but this affects neither the calling code nor the iteration over the list.
You can give ListInitialized a variadic constructor template:
struct ListInitialized
{
template <class... T>
ListInitialized(const T... &arg);
};
If you need to make sure it can only be instantiated with the correct type, consider suitable SFINAE:
struct ListInitialized
{
template <
class... T,
class Sfinae = std::enable_if_t<std::is_same<std::decay_t<T>, NonCopyable> &&...
>
ListInitialized(const T... &arg);
};
In addition to the comments and answers above, I found that this minimalistic wrapper fulfills my needs:
#include <initializer_list>
#include <utility>
struct S {
S() { }
S(const S&) = delete; // Non-copyable
void f() const { }
};
template<class T>
class const_reference_wrapper {
public:
const_reference_wrapper(const T& ref_) : ref(ref_) { }
operator const T&() const { return ref; }
private:
const T& ref;
};
struct T {
T(std::initializer_list<const_reference_wrapper<S>> l) : c(l.size()) {
for(const S& i : l) // note: const auto& can't be used here, but it would be the same for std::reference_wrapper
i.f(); // we can do something with the elements
}
int c;
};
int main() {
S a, b;
T t{a, b, S{}}; // we can mix lvalues and rvalues with a natural syntax
return t.c; // correctly returns 3
}
Of course, care needs to be taken to ensure that any rvalue passed through this will live through the time it is being referenced.
So I have a couple of functions, which I get a magic function object, that provides overload resolution on:
void foo1();
void foo2(int);
auto foo_ptr = boost::hana::overload(foo1,foo2);
//Later
foo_ptr(12); //Both Valid (Yeah!)
foo_ptr();
But the problem arises when I do something like this:
using ptr_t = decltype(foo_ptr);
struct mine
{
ptr_t ptr;
mine(ptr_t ptr) : ptr(ptr){}
};
mine m(foo_ptr);
When I try to compile this code I get error: no matching function for call to 'boost::hana::overload_t<void (*)(int)>::overload_t()'.
See for yourself on godbolt.....
Now my question is this:
Am I allowed to copy these overload objects around (the documentation for hana does not say one way or the other), and if so why does it fail when I put it in a class as a member?
This is arguably a bug in Boost.Hana. The constructor of overload_t is:
template <typename F_, typename ...G_>
constexpr explicit overload_t(F_&& f, G_&& ...g)
: overload_t<F>::type(static_cast<F_&&>(f))
, overload_t<G...>::type(static_cast<G_&&>(g)...)
{ }
Notice that these are unconstrained forwarding references. This is the common problem of having a forwarding reference constructor be a better match than the copy constructor, when you have a non-constant object. Reduced example:
struct X {
X();
template <typename F> X(F&&);
X(X const&) = default;
};
X x1;
X x2(x1); // does *not* call the copy constructor
However, if the object were const, then the copy constructor would be a better match. So the short fix is to just do that:
struct mine
{
ptr_t ptr;
mine(ptr_t const& ptr) : ptr(ptr){}
};
And now it compiles. Likewise, as Yakk suggests, move instead of copy would also work for the same reason:
struct mine
{
ptr_t ptr;
mine(ptr_t ptr) : ptr(std::move(ptr)) {}
};
I have a struct that manages 'views' into an array of variable type. The purpose of this is to provide a unified state vector for ODE simulation while at the same time working on individual segments of this vector from several other classes in an organized fashion. If this triggers a design pattern in your mind, please let me know.
My issue is that the first implementation ContainerHolderFirst, using Cont::pointer does not compile for const arrays.
My next attempt with std::conditional, mixing in Cont::const_pointer still doesn't work.
Only the third attempt with std::conditional and modification of Cont::value_type compiles (and seems to work in my larger project).
My questions are the following:
It would be nice if ContainerHolderFirst would work. I'd wish that const-ness of the type would be propagated to the pointer. Why isn't it?
I understand even less why ContainerHolderSecond doesn't work. The explanation in https://stackoverflow.com/a/1647394/1707931 rather suggests that this is the way to go, no? It does actually work. My bad.
Are there issues with the third approach that I haven't uncovered yet? Is there a simpler way?
Full C++11 code follows:
Update1: Fixing ContainerHolderSecond. It does compile with correct initialization. Also added ContainerHolderBarry suggested by Barry using decltype and declval.
This leaves the question whether any of the approaches are preferred? Will they lead to performance differences? They should all compile to the same object, no?
#include <iostream>
#include <array>
template <typename Cont>
class ContainerHolderFirst {
Cont& data_;
const static size_t offset_ = 1;
typename Cont::pointer data_view;
public:
ContainerHolderFirst(Cont& data) : data_(data), data_view(&data[offset_]) {}
};
template <typename Cont>
class ContainerHolderSecond {
using Pointer = typename std::conditional<std::is_const<Cont>::value,
typename Cont::const_pointer,
typename Cont::pointer>::type;
Cont& data_;
const static size_t offset_ = 1;
Pointer data_view;
public:
ContainerHolderSecond(Cont& data) : data_(data), data_view(&data[offset_]) {}
};
template <typename Cont>
class ContainerHolderBarry {
using Pointer = decltype(&std::declval<Cont&>()[0]);
Cont& data_;
const static size_t offset_ = 1;
Pointer data_view;
public:
ContainerHolderBarry(Cont& data) : data_(data), data_view(&data[offset_]) {}
};
int main() {
using namespace std;
array<int, 2> my_array;
ContainerHolderFirst<array<int, 2>> holder(my_array); // works
const array<int, 2> const_array{5,7};
// ContainerHolderFirst<const array<int, 2>> const_holder(const_array);
/* error: invalid conversion from 'const value_type* {aka const int*}' to 'std::array<int, 2ull>::pointer {aka int*}' [-fpermissive] */
ContainerHolderSecond<array<int,2>> second_holder(my_array); // works!
ContainerHolderSecond<const array<int,2>> const_holder(const_array); //updated; works as well; awkward
ContainerHolderThird<array<int,2>> third_holder(my_array); // still works
ContainerHolderThird<const array<int,2>> third_const_holder(const_array); //finally compiles as well
ContainerHolderBarry<array<int,2>> barry_holder(my_array);
ContainerHolderBarry<const array<int,2>> barry_const_holder(const_array);
}
You're making this unnecessarily difficult on yourself. If you want the type of &cont[offset], just ask for the type of that expression. Use std::declval along with decltype:
template <typename Cont>
class ContainerHolder {
using Pointer = decltype(&std::declval<Cont&>()[0]);
...
};
The only problem with ContainerHolderSecond is that you're using it incorrectly:
ContainerHolderSecond<array<int,2>> const_holder(const_array);
// ^-- insert "const" here
As for ContainerHolderFirst, the reason that array<T, N>::pointer is the same type as (array<T, N> const)::pointer is that there is no automatic way to determine where the const qualification should be added to the nested type, and there is no language facility to describe this (that is, we don't have const-qualified typedefs or type aliases).
I think the most simple way to ask is due to an example. Assume we have the following type:
class Node
{
// make noncopyable
Node(const Node& ref) = delete;
Node& operator=(const Node& ref) = delete;
// but moveable
Node(Node&& ref) = default;
Node& operator=(Node&& ref) = default;
// we do not have a default construction
Node() = delete;
Node(unsigned i): _i(i) {}
unsigned _i;
};
Now i want to store some of these Nodes in a std::array:
template<unsigned count>
class ParentNode
{
std::array<Node,count> _children;
ParentNode()
// i cannt do this, since i do not know how many nodes i need
// : _children{{Node(1),Node(2),Node(3)}}
: _children() // how do i do this?
{}
};
As stated in the comment, the question is: How do I do this? The unsigned passed to the child should be the index of the array where the child is stored. But more general solutions are also very appreciated!
The following solution I found myself might end up in undefined behavior for more complex types. For a proper well defined solution see accepted answer.
template<unsigned count>
class ParentNode
{
public:
// return by value as this will implicitly invoke the move operator/constructor
std::array<Node,count> generateChildren(std::array<Node,count>& childs)
{
for (unsigned u = 0; u < count; u++)
childs[u] = Node(u); // use move semantics, (correct?)
return std::move(childs); // not needed
return childs; // return by value is same as return std::move(childs)
}
std::array<Node,count> _children;
ParentNode()
// i cannt do this, since i do not know how many nodes i need
// : _children{{Node(1),Node(2),Node(3)}}
: _children(generateChildren(_children)) // works because of move semantics (?)
{}
};
ParentNode<5> f;
The code does compile. But I am not sure if it does what i expect it to do. Maybe someone who has good insight in move semantics and rvalue references can just add some comments:-)
You can use a variadics to generate an array with elements initialized to an arbitrary function of the indices. Using the standard machinery for generating index sequences:
template <int... I> struct indices {};
template <int N, int... I> struct make_indices :
make_indices<N-1,N-1,I...> {};
template <int... I> struct make_indices<0,I...> : indices<I...> {};
it's fairly straightforward:
template <typename T, typename F, int... I>
inline std::array<T, sizeof...(I)> array_maker(F&& f, indices<I...>) {
return std::array<T, sizeof...(I)>{ std::forward<F>(f)(I)... };
}
template <typename T, std::size_t N, typename F>
inline std::array<T, N> array_maker(F&& f) {
return array_maker<T>(std::forward<F>(f), make_indices<N>());
}
Which lets us do anything from duplicating the effect of std::iota:
auto a = array_maker<int,10>([](int i){return i;});
to making an array with the squares of the first 10 natural numbers in reverse order:
const auto a = array_maker<std::string,10>([](int i){
return std::to_string((10 - i) * (10 - i));
});
Since your Node is movable, this allows you to define your ParentNode constructor as:
ParentNode()
: _children(array_maker<Node, count>([](unsigned i){return i+1;}))
{}
See it all put together live at Coliru.
Really, there's not much you can do. You painted yourself into a corner by wanting to put a type with no default constructor into an array of a size determined by a template parameter, and then wanting to initialize the elements with some arbitrary values.
There is nothing you can return from a function which can be put into a braced-init-list and used to initialize an array (or aggregate of any kind) with more than one element. {} doesn't mean "initializer_list". It's a braced-init-list, which can become an initializer_list under certain circumstances, but it can also become the parameters for a constructor call or the elements to use in aggregate initialization.
Your best bet is really to just use a vector and initialize it manually with a loop.