C++ concepts: Some signatures function conversion - c++

Unfortunately, the only tutorial that I have found about concepts was the concept lite tutorial (and it was really basic). And even with the technical specification, there is some signatures function that I don't know how translate into concepts (maybe just because my English is bad and I can't read the technical specification really well).
So there is a list of signatures function I still don't know how to "translate":
CFoo --> class CFoo {};
void Foo1() const;
CFoo& Foo2();
void Foo3(CFoo&);
{static, friend, ... } void Foo4();
template < typename ... Args >
void Foo5(Args && ... args);
I want to have some kind of interface for a class with these functions.
Don't even know if it's possible at this point. Foo2 and Foo3 seems to be the same problem.
Honestly I really want to know Foo2 and Foo5.
I tried somethings, for the Foo2 but I don't have any idea about Foo5:
class Handle {};
template < typename Object >
concept bool C_Object =
requires(Handle handle) {
{get(handle)} -> Object&
};
template < C_Object Object >
class Foo {
Object obj;
};
int main() {
Foo<int> test;
return 0;
}
I know this won't compile because Foo don't have a get menber, but these are not the right errors:
Test1.cpp:6:16: error: there are no arguments to ‘get’ that depend on a template parameter, so a declaration of ‘get’ must be available [-fpermissive]
{get(handle)} -> Object&
^
Test1.cpp:6:16: note: (if you use ‘-fpermissive’, G++ will accept your code, but allowing the use of an undeclared name is deprecated)
Test1.cpp: In function ‘int main()’:
Test1.cpp:18:10: error: template constraint failure
Foo<int> test;
^
Test1.cpp:18:10: note: constraints not satisfied
Test1.cpp:4:14: note: within ‘template<class Object> concept const bool C_Object<Object> [with Object = int]’
concept bool C_Object =
^~~~~~~~
Test1.cpp:4:14: note: with ‘Handle handle’
Test1.cpp:4:14: note: the required expression ‘get(handle)’ would be ill-formed
If someone can point me out some resources or, why not, a solution. It will be great.
Have a great day

I know this won't compile because Foo don't have a get menber […]
Concepts deal with normal expressions. In particular, the scope of requires expressions is normal scope, not class scope. This may be more apparent with this concept:
template<typename Lhs, typename Rhs>
concept bool Addable = requires(Lhs lhs, Rhs rhs) {
lhs + rhs;
};
Addable<int, long> is fulfilled because given int lhs; long rhs; then lhs + rhs is a valid expression. We're using the built-in addition operator on two (pretend) variables we explicitly introduced in the parameter list, not calling a member operator+ on an implicit *this.
Concepts are about interfaces in the wider sense (as in 'API'), not in a narrower OOP sense. You can think of Addable as a relation on pairs of types. That Addable<int, long> holds doesn't mean int on its own has a special relationship with Addable. It is true that Addable can be used as e.g.
template<Addable<long> Var>
struct client {
Var var;
};
and then client<int> comes with an Addable<int, long> constraint, but this shortcut is syntactical in nature. It's a helpful way of cutting down boilerplate, namely sparing us from writing template<typename Var> requires Addable<Var, long>.
With that in mind, here are some expressions that may come close to checking the member signatures you mentioned, plus the Handle scenario:
template<typename Obj>
concept bool Object = requires(Obj obj, Obj const cobj, Handle handle) {
cobj.Foo1();
{ obj.Foo2() } -> Obj&;
obj.Foo3(obj);
// static
Obj::Foo4();
// non-member, possibly friend
Foo4(obj);
{ obj.get(handle) } -> Obj&;
};
(I elided the Foo5 scenario because it's worth its own question, here is a lead.)

Related

Template func with cond. const argument + template arg deduction

I am implementing a wrapper class that wraps some base class object. I want the wrapper to be as unnoticeable as possible and therefore, I have already overloaded the -> operator to return a reference to the wrapped object in order to provide immediate access to the base-class's interface.
Additionally, I want my wrapper type to also implement all operators that the base-class implements and just delegates the respective operator calls to the wrapped object. However, since I want to be able to wrap any arbitrary base-class, I don't know a priori what operators are defined for the wrapped class.
My current approach consists of just defining all operators for my wrapper and using SFINAE to disable those implementations that are not covered by base-class implementations.
The implementation for the operator overload essentially always looks like this
auto operator <operator> (const Wrapper &lhs, const Wrapper &rhs) { return lhs.get() <operator> rhs.get(); }
auto operator <operator> (const Wrapper &lhs, const Base &rhs) { return lhs.get() <operator> rhs; }
auto operator <operator> (const Base &lhs, const Wrapper &rhs) { return lhs <operator> rhs.get(); }
where <operator> is the respective operator. Since, I don't want to duplicate this code for all operators, I have defined some macros to create the definitions for me.
This works for most operators, but now I also want to support the various assignment operators (e.g. *=). Here, the lhs argument must not be const as it is modified via the action of the operator.
I could just generate these separately, but I thought that there must be a way to simply make the lhs argument const or non-const depending on a constexpr boolean (available as macro-parameter). Thus, I created a templated helper cond_add_const< T, bool > that returns const T if passed true and T if passed false.
The problem now is, that the overloads that involve the Base class as direct parameters fail to be resolved due to template argument deduction failure. The problem seems to be that in order to apply my conditional const, I essentially have the replace the respective type with cond_add_const< T, true >::type and apparently everything to the left of :: does not participate in template argument deduction.
This seems rather frustrating and it also feels like for my simple case there should be a possibility to circumvent this limitation (or choose an approach that does not require it), but I just can't come up with one (that does not involve duplicating code). Any ideas?
MWE illustrating my problem:
#include <type_traits>
template< typename T, bool is_const > struct cond_add_const {
using type = typename std::conditional< is_const, typename std::add_const< T >::type, typename std::remove_const< T >::type >::type;
};
template< typename T > void myFunc(typename cond_add_const< T, true >::type lhs, int rhs) {
}
int main() {
myFunc(1, 2);
}
Compiling this snippet with g++ results in
test.cpp: In function ‘int main()’:
test.cpp:11:13: error: no matching function for call to ‘myFunc(int, int)’
11 | myFunc(1, 2);
| ^
test.cpp:7:29: note: candidate: ‘template<class T> void myFunc(typename cond_add_const<T, true>::type, int)’
7 | template< typename T > void myFunc(typename cond_add_const< T, true >::type lhs, int rhs) {
| ^~~~~~
test.cpp:7:29: note: template argument deduction/substitution failed:
test.cpp:11:13: note: couldn’t deduce template parameter ‘T’
11 | myFunc(1, 2);
|
You might turn your template functions in non-template friend function of your class:
template <typename T>
struct Wrapper
{
// ...
friend void myFunc(typename cond_add_const<Wrapper<T>, true >::type lhs, T rhs)
{ /*...*/ }
};
Demo
There are some caveats:
the friend function can only be found via "ADL" (So at least one parameter should be of the Wrapper type).

Lazy type constraint (implement Rust `Default` trait in c++)

In Rust you can implement traits for structs and each implementation has own type constraint. (for who may does not familiar with rust, you can consider a "trait" as a "base class" and "implementation" as "inheritance").
look at this example:
// our struct has an item of type mutable T pointer
struct Box<T> {
inner: mut* T
}
// implementing `Default` trait for
// box. in this implementation, type
// constraint is: `T: Default`.
// it means the inner type also must
// implements Default.
impl <T: Default> for Box<T> {
fn default() -> Self {
return Box::new(T::default());
}
}
the note in this example is there is no need T: Default to be applied until you use Box::default() in your code.
well it is possible to do like this in cpp? I'm familiar with how we can constraint types in cpp and this is not my problem. I want to know is there some way to lazy type constrain (maybe a better description) in cpp when I define a class?
I know it is possible with if constexpr or static_assert. but this way is not beautiful.
I want a template or concept solution ( I mean what applies to function signature in fact) if possible.
thank you for any guidance.
I am not sure I follow exactly what you want (I don't really know Rust), but you can constrain member functions individually:
template<typename T>
concept Default = requires {
{ T::default_() } -> std::same_as<T>;
};
template<typename T>
struct Box {
static Box default_()
requires Default<T> {
//...
}
//...
};
Now Box itself has no requirements on T, but to use Box::default() will require T to satisfy the Default concept.
However, it would basically work without the constraint as well. If Box<T>::default calls T::default() and the latter is not well-formed the code will fail to compile if and only if Box<T>::default is actually used in a way that requires it to be defined (i.e. called or pointer/reference to it taken). But without the requires clause e.g. Default<Box<T>> would always report true.
And of course in C++ we would use the default constructor instead of a static member function called default to construct the object. The same approach applies to constructors though and there is already the concept std::default_initializable for that in the standard library.
As far as I understand, Rust traits do not really overlap fully with either C++ (abstract) base classes nor concepts though. The approach above will not allow for runtime polymorphism on types satisfying Default. For that an abstract base class with the interface functions as non-static pure virtual member functions should be used instead.
However, the trait Default only imposes a requirement on a static member function, so that it shouldn't be relevant to runtime polymorphism, but only compile-time properties of the type, which is what concepts are for, although in contrast to Rust you don't declare a type to implement a trait. Instead the concept describes requirements which a type needs to satisfy to satisfy the concept.
Yes, it can be done with concepts:
#include <concepts>
template<std::default_initializable T>
struct Box {};
struct Yes{};
struct No{ No(int){}}; // No default ctor
int main()
{
Box<Yes> y;
Box<No> n;
}
which outputs:
<source>:14:11: error: template constraint failure for 'template<class T> requires default_initializable<T> struct Box'
14 | Box<No> n;
| ^
<source>:14:11: note: constraints not satisfied
In file included from <source>:1:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/concepts: In substitution of 'template<class T> requires default_initializable<T> struct Box [with T = No]':
<source>:14:11: required from here
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/concepts:136:13: required for the satisfaction of 'constructible_from<_Tp>' [with _Tp = No]
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/concepts:141:13: required for the satisfaction of 'default_initializable<T>' [with T = No]
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/concepts:137:30: note: the expression 'is_constructible_v<_Tp, _Args ...> [with _Tp = No; _Args = {}]' evaluated to 'false'
137 | = destructible<_Tp> && is_constructible_v<_Tp, _Args...>;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Box<Yes> will compile without issues. The above is a shorthand for the following which allows to chain more requirements
template<typename T>
requires std::default_initializable<T>
struct Box { };
#include <type_traits>
#include <concepts>
class SomeBaseClass {};
template<typename T>
concept Derived = std::is_base_of_v<SomeBaseClass, T>;
template<typename T>
concept Default = requires(T) {
{ T::by_default() } -> std::convertible_to<T>;
};
template<Derived T>
class Box {
T* inner;
Box(T data): (&data);
public:
static Box by_default()
requires(Default<T>) { // This was my lost!
return Box(T::by_default());
}
};
class Object: SomeBaseClass {
public:
static Object by_default() {
return Object();
}
};
int main() {
Box<Object> b = Box<Object>::by_default();
}

std::add_const_t<T>& vs const T& [duplicate]

Some type transformations in <type_traits> can also be expressed using core language syntax (e.g. std::add_const<T>::type is/seems equivalent to const T). Dtto for std::add_lvalue_reference, and perhaps others. What is the use for these type traits?
I fully understand the standard would be providing an "incomplete toolbox" without them, and I can imagine use in a meta way, something like this:
template<typename In, template <typename> class Modifier>
struct Apply {
typedef typename Modifier<T>::type Out;
};
Apply<int, std::add_const>
Are there any other use cases for these traits which can be expressed syntactically, or are they just included "out of a sense of completeness" and for the occasional meta-use?
Those traits come from Boost and the proposal to add them to the standard, N1345, quotes Andrei Alexandrescu as saying:
"I understand the symmetry argument for adding add_const, add_volatile, add_cv, and add_pointer, however I would argue in favor of eliminating them. The language-provided equivalents are just simpler and nicer."
The same proposal also gives this rationale:
Author's Note: superficially the add_const, add_volatile and add_cv classes are irrelevant, since, for example, add_const::type is the same as T const, for all T (currently this does not apply to function types - but issue 295 addresses this). However the experience from boost is that several users have asked for these templates to be present in the library for the following reasons: (a) Some users find these more explicit - when combining transformation templates in particular, users like the kind of "built in documentation" that these templates provide. (b) Not all users are aware that cv-qualifying a reference is allowed and has no effect, or that cv-qualifying a type that is already cv-qualified is allowed and has no effect. (c) Compilers may emit warnings when cv-qualifying a type that is a reference, or already has a cv-qualifier, these templates can be implemented such that these messages are suppressed in these cases.
Also, for add_reference (renamed to add_lvalue_reference in the standard):
Author's Note: the add_reference template was one of the original motivations behind the boost type traits library. However the resolution to issue 106 makes the template appear largely redundant. In spite of that add_reference may well be useful in suppressing compiler warnings when inadvertently creating references to references in template code.
These traits are provided for occasional meta-use. It makes it possible to transport wanted cv-qualifiers around in meta programming.
template<class T,template<class> class Trait>
struct transform
{
/* working with T creating newT*/
typedef Trait<newT>::type type;
};
template<class T>
struct special_transform
: transfrom<T, std::add_const>
{};
In this case you could not replace std::add_const with const.
add_const can be used to resolve type deduction conflicts.
template <typename T>
class wrapper;
template <typename T>
bool operator==(wrapper<T> const& w, T const& t);
Problems arise if we use wrapper<T const>:
wrapper<int const> w = { 42 };
assert(w == 42); // Error: conflicting deduced types
T is simultaneously deduced to be both int and int const. This can be resolved using add_const:
template <typename T>
bool operator==(wrapper<T> const& w, add_const_t<T>& t);
The only use case I know is illustrated below:
struct F
{
bool f() const { return true; }
bool f() { return false; }
};
assert(!F{}.f())
assert(std::add_const_t< F >{}.f());
It also needed for testing of cv-ref-qualified member-functions functionality, that may differs for different overloadings (only for lref-qualified modern C++ has handy std::as_const function):
struct F
{
int g() & { return 1; }
int g() const & { return 2; }
int g() && { return 3; }
int g() const && { return 4; }
};
F f;
assert(f.g() == 1);
assert(std::as_const(f).g() == 2);
assert(F{}.g() == 3);
assert(std::add_const_t< F >{}.g() == 4); // rarely needed, but if needed, then it helps

non-member operator<() for template class's nested class not matched by compiler

I'm having trouble with a templated comparison operator for comparing instances of a type dependent on the template argument. I've been scouring the tubes for a while, including SO, and struggling to understand what I'm doing wrong. I believe it's an issue with dependent name lookup, but that's not helping me get the code working :-(
Here's a minimal testcase:
template<typename T>
struct Foo
{
struct Bar {
T t;
};
Bar b;
};
template<typename T>
inline bool operator<(const typename Foo<T>::Bar& b1, const typename Foo<T>::Bar& b2)
{ return b1.t < b2.t; }
int main()
{
Foo<int> f1, f2;
return (f1.b < f2.b? 0 : 1);
}
Compilation gives:
templated-comparison-op.cpp: In function ‘int main()’:
templated-comparison-op.cpp:20:20: error: no match for ‘operator<’ in ‘f1.Foo<int>::b < f2.Foo<int>::b’
templated-comparison-op.cpp:20:20: note: candidate is:
templated-comparison-op.cpp:13:13: note: template<class T> bool operator<(const typename Foo<T>::Bar&, const typename Foo<T>::Bar&)
For now I've got the comparison operator as a member of the template class, which works fine, so no urgency. I would like to understand what I'm missing here though; can anyone enlighten me?
Edit: That's not actually an accurate testcase; in the real code I extracted this case from, template class Foo is nested inside another class, and this is why the 'typename' keywords are required. As answers below state, in the code above 'typename' would be unnecessary.
Edit: I've replaced the original testcase with a new one highlighting the current problem, and also updated the question title, since I'm struggling to replicate the exact error I get in the real codebase. If there's a workaround for the issue in the testcase then perhaps that'll get the equivalent in real code closer to compilation, so still interested in pursuing this.
The problem is that your operator< is not usable because the compiler cannot automatically find out what T is.
You can solve your problem by declaring a non-template operator<
template<typename T>
struct Foo
{
struct Bar {
T t;
};
Bar b;
friend bool operator<(const Bar& b1, const Bar& b2)
{ return b1.t < b2.t; }
};
The operator< declaration is then found by argument dependent lookup for operator< for the expression x < y.
Alternatively you can put the operator< definition inside the definition Bar (but this doesn't really matter in this case, since Foo<T> is an associated class of Foo<T>::Bar).
You don't need the typenames in the function declaration, since Foo<T> isn't a dependent type:
template<typename T>
bool operator<(const Foo<T>& f1, const Foo<T>& f2)
{ return f1.t < f2.t; }
(I'm not sure why they produce that particular error, but removing them fixes it).
Don't know if that's the problem, but the typename in the parameter list shouldn't be there. (If that's the problem, it's a horrible error message.)

Can C++ templates check if a function has been overloaded for a given type?

I have a template function that I expect to be templatized for different types at different places.
The problem is that I would like to know at compile time if there is an specialization for the given type to generate in 2 different ways another template.
template<typename T>
bool tobool(const T&){ throw Exception("Can't cast to bool");};
template<> bool tobool<bool>(const bool &value){ return value;}
I know you can test for function existance like in here.
Any chance on how to test if tobool has been specialized?
Imagine that I want to generate a isbool() that returns true if tobool() has been specialized and returns false if not.
As a (somewhat ugly and brittle) workaround, you could require specialization of a struct rather than a function and include a class constant to indicate whether the struct has been specialized:
template <typename T>
struct ToBool {
static bool tobool(const T&);
static const bool specialized = false;
};
Another option is to only define tobool in specializations. That way, ToBool<Foo>::tobool(f) won't compile for any classes Foo that ToBool hasn't been specialized for.
As an alternative to tobool, you can use explicit conversion operators if you have control over the classes to be converted.
class Foo {
public:
operator bool();
...
};
...
Foo f;
if (f) ...
If the type doesn't have a bool conversion (well, a conversion to a numeric or pointer type, both of which have standard conversions to bool), the program won't compile. Voila, compile time checking for a conversion.
If you don't want implicit conversion to bool, you can define an operator! and use a double-bang for explicit conversion (though this isn't as readable):
class Foo {
public:
bool operator!();
...
};
...
Foo f;
if (!!f) ...
The answer to your specific question is this: No, you cannot check whether T is using the primary or the specialized template. #Martin York's question is a very good one: why on earth would you want to check that? :)