I have found a very strange issue when revisiting and updating any old library. I have the following code
class bmint_tmp;
class bmfloat_tmp;
template<typename T> struct bop_return{typedef bmint_tmp type;};
template<> struct bop_return<float>{typedef bmfloat_temp type;};
class bmint
{
template<typename T> friend typename bop_return<T>::type operator+(const T& l, const bmint& r);
/** irrelevant code **/
};
template<typename T> typename bop_return<T>::type operator+(const T& l, const bmint& r)
{
return r.operator+(l);
}
template<> typename bop_return<bmint_tmp>::type operator+(const bmint_tmp& l, const bmint_tmp& r)=delete;
I have deleted this template instantiation because I prefer to get called the existing bmint_tmp::operator+, implemented as member method in another file. But, when compiling it seems that gcc dont see nothing but this deleted operator, and says:
error: use of deleted function 'typename::bigmath::bop_return::type bigmath::operator+(const T&, const bigmath::bmint&) [with T=...
I have attempted to change modifiers (remove const specification, for example), but my many attempts are unsuccessful. Can anyone help? Thanks in advance.
Yeah, if a function is deleted then it still exists (and it's even considered to be defined!), with everything that implies… you just can't actually call it.
Use enable_if, instead, so that the template specialisation cannot be instantiated at all. Then the only candidate will be the operator function you want.
Related
I have a function that needs to take shared ownership of an argument, but does not modify it.
I have made the argument a shared_ptr<const T> to clearly convey this intent.
template <typename T>
void func(std::shared_ptr<const T> ptr){}
I would like to call this function with a shared_ptr to a non-const T. For example:
auto nonConstInt = std::make_shared<int>();
func(nonConstInt);
However this generates a compile error on VC 2017:
error C2672: 'func': no matching overloaded function found
error C2784: 'void func(std::shared_ptr<const _Ty>)': could not deduce template argument for 'std::shared_ptr<const _Ty>' from 'std::shared_ptr<int>'
note: see declaration of 'func'
Is there a way to make this work without:
Modifying the calls to func. This is part of a larger code refactoring, and I would prefer not to have to use std::const_pointer_cast at every call site.
Defining multiple overloads of func as that seems redundant.
We are currently compiling against the C++14 standard, with plans to move to c++17 soon, if that helps.
template <typename T>
void cfunc(std::shared_ptr<const T> ptr){
// implementation
}
template <typename T>
void func(std::shared_ptr<T> ptr){ return cfunc<T>(std::move(ptr)); }
template <typename T>
void func(std::shared_ptr<const T> ptr){ return cfunc<T>(std::move(ptr)); }
this matches how cbegin works, and the "overloads" are trivial forwarders with nearly zero cost.
Unfortunately, there is no good solution to what you desire. The error occurs because it fails to deduce template argument T. During argument deduction it attempts only a few simple conversations and you cannot influence it in any way.
Think of it: to cast from std::shared_ptr<T> to some std::shared_ptr<const U> it requires to know U, so how should compiler be able to tell that U=T and not some other type? You can always cast to std::shared_ptr<const void>, so why not U=void? So such searches aren't performed at all as in general it is not solvable. Perhaps, hypothetically one could propose a feature where certain user-explicitly-declared casts are attempted for argument deduction but it isn't a part of C++.
Only advise is to write function declaration without const:
template <typename T>
void func(std::shared_ptr<T> ptr){}
You could try to show your intent by making the function into a redirection like:
template <typename T>
void func(std::shared_ptr<T> ptr)
{
func_impl<T>(std::move(ptr));
}
Where func_impl is the implementation function that accepts a std::shared_ptr<const T>. Or even perform const cast directly upon calling func_impl.
Thanks for the replies.
I ended up solving this a slightly different way. I changed the function parameter to just a shared_ptr to any T so that it would allow const types, then I used std::enable_if to restrict the template to types that I care about. (In my case vector<T> and const vector<T>)
The call sites don't need to be modified. The function will compile when called with both shared_ptr<const T> and shared_ptr<T> without needing separate overloads.
Here's a complete example that compiles on VC, GCC, and clang:
#include <iostream>
#include <memory>
#include <vector>
template<typename T>
struct is_vector : public std::false_type{};
template<typename T>
struct is_vector<std::vector<T>> : public std::true_type{};
template<typename T>
struct is_vector<const std::vector<T>> : public std::true_type{};
template <typename ArrayType,
typename std::enable_if_t<is_vector<ArrayType>::value>* = nullptr>
void func( std::shared_ptr<ArrayType> ptr) {
}
int main()
{
std::shared_ptr< const std::vector<int> > constPtr;
std::shared_ptr< std::vector<int> > nonConstPtr;
func(constPtr);
func(nonConstPtr);
}
The only downside is that the non-const instantiation of func will allow non-const methods to be called on the passed-in ptr. In my case a compile error will still be generated since there are some calls to the const version of func and both versions come from the same template.
As the const is only for documentation, make it a comment:
template <class T>
void func(std::shared_ptr</*const*/ T> p) {
}
You could additionally delegate to the version getting a pointer to constant object if the function is hefty enough to make it worthwhile:
template <class T>
void func(std::shared_ptr</*const*/ T> p) {
if (!std::is_const<T>::value) // TODO: constexpr
return func<const T>(std::move(p));
}
No guarantee the compiler will eliminate the move though.
You certainly don't want to be modifying the call sites, but you sure can be modifying the functions themselves - that's what you implied in the question anyway. Something had to be changed somewhere, after all.
Thus:
In C++17 you could use deduction guides and modify call sites, but in a less intrusive manner than with a cast. I'm sure a language lawyer can pitch in about whether adding a deduction guide to the std namespace is allowed. At the very least we can limit the applicability of those deduction guides to the types we care about - it won't work for the others. That's to limit potential for mayhem.
template <typename T>
class allow_const_shared_ptr_cast : public std::integral_constant<bool, false> {};
template <typename T>
static constexpr bool allow_const_shared_ptr_cast_v = allow_const_shared_ptr_cast<T>::value;
template<>
class allow_const_shared_ptr_cast<int> : public std::integral_constant<bool, true> {};
template <typename T>
void func(std::shared_ptr<const T> ptr) {}
namespace std {
template<class Y> shared_ptr(const shared_ptr<Y>&) noexcept
-> shared_ptr<std::enable_if_t<allow_const_shared_ptr_cast_v<Y>, const Y>>;
template<class Y> shared_ptr(shared_ptr<Y>&&) noexcept
-> shared_ptr<std::enable_if_t<allow_const_shared_ptr_cast_v<Y>, const Y>>;
}
void test() {
std::shared_ptr<int> nonConstInt;
func(std::shared_ptr(nonConstInt));
func(std::shared_ptr(std::make_shared<int>()));
}
std::shared_ptr is certainly less wordy than std::const_pointer_cast<SomeType>.
This should not have any performance impact, but sure modifying the call sites is a pain.
Otherwise there's no solution that doesn't involve modifying the called function declarations - but the modification should be acceptable, I think, since it's not any more wordy than what you had already:
I think the problem is fairly common, so there should be a known solution. I came up with one, but I'm not really satisfied, so I'm asking here, hoping someone can help.
Say I have a function, whose signature is
template<typename T>
void foo(const MyArray<const T>& x);
The const in the template parameter is to prevent me from changin the array content, since (for reasons beyond this question), the accessors ([] and ()) of MyArray<T> are always marked const, and return references to T (hence, the const ensure safety, since MyArray<T>::operator[] returns T&, while MyArray<const T>::operator[] returns const T&).
Great. However, templates with different template arguments are non related, so I can't bind a reference to MyClass<T> to a reference of MyClass<const T>, meaning I can't do this
MyArray<double> ar(/*blah*/);
foo(ar);
Notice that, without a reference, the code above would work provided that there is a copy constructor that lets me create MyArray<const T> from MyArray<T>. However, I don't want to remove the reference, since the array construction would happen a lot of times, and, despite relatively cheap, its cost would add up.
So the question: how can I call foo with an MyArray<T>?
My only solution so far is the following:
MyArray<T> ar(/*blah*/);
foo(reinterpret_cast<MyArray<const T>>(ar));
(actually in my code I hid the reinterpret cast in an inlined function with more verbose name, but the end game is the same). The class MyArray does not have a specialization for const T that makes it not reinterpretable, so the cast should be 'safe'. But this is not really a nice solution to read. An alternative, would be to duplicate foo's signature, to have a version taking MyArray<T>, which implementation does the cast and calls the const version. The problem with this is code duplication (and I have quite a few functions foo that need to be duplicated).
Perhaps some extra template magic on the signature of foo? The goal is to pass both MyArray<T> and MyArray<const T>, while still retaining const-correctness (i.e., make the compiler bark if I accidentally change the input in the function body).
Edit 1: The class MyArray (whose implementation is not under my control), has const accessors, since it stores pointers. So calling v[3] will modify the values in the array, but not the members stored in the class (namely a pointer and some smart-pointer-like metadata). In other words, the object is de facto not modified by accessors, though the array is. It's a semantic distinction. Not sure why they went this direction (I have an idea, but too long to explain).
Edit 2: I accepted one of the two answers (though they were somewhat similar). I am not sure (for reasons long to explain) that the wrapper class is doable in my case (maybe, I have to think about it). I am also puzzled by the fact that, while
template<typename T>
void foo(const MyArray<const T>& x);
MyArray<int> a;
foo(a);
does not compile, the following does
void foo(const MyArray<const int>& x);
MyArray<int> a;
foo(a);
Note: MyArray does offer a templated "copy constructor" with signature
template<typename S>
MyArray(const MyArray<S>&);
so it can create MyArray<const T> from MyArray<T>. I am puzzled why it works when T is explicit, while it doesn't if T is a template parameter.
I would stay with
template<typename T>
void foo(const MyArray<T>&);
and make sure to instantiate it with const T (in unitTest for example).
Else you can create a view as std::span.
Something like (Depending of other methods provided by MyArray, you probably can do a better const view. I currently only used operator[]):
template <typename T>
struct MyArrayConstView
{
MyArrayConstView(MyArray<T>& array) : mArray(std::ref(array)) {}
MyArrayConstView(MyArray<const T>& array) : mArray(std::ref(array)) {}
const T& operator[](std::size_t i) {
return std::visit([i](const auto& a) -> const T& { return a[i]; }), mArray);
}
private:
std::variant<std::reference_wrapper<MyArray<T>>,
std::reference_wrapper<MyArray<const T>>> mArray;
};
and then
template <typename T>
void foo(const MyArrayConstView<T>&);
but you need to call it explicitly, (as deduction won't happen as MyArray<T> is not a MyArrayConstView)
MyArray<double> ar(/*blah*/);
foo(MyArrayConstView{ar});
foo<double>(ar);
Since you are not allowed to change MyArray, one option is to use an adapter class.
template <typename T>
class ConstMyArrayView {
public:
// Not an explicit constructor!
ConstMyArrayView(const MyArray<T>& a) : a_(a) {}
const T& operator[](size_t i) const { return a_[i]; }
private:
const MyArray<T>& a_;
};
template<typename T>
void foo(const ConstMyArrayView<T>& x);
MyArray<T> x;
foo(x);
But in the end, if you can change MyArray to match the const-correctness you want, or switch to a class that does, that'll be the better option.
Here's an ugly but effective way to have a function use one type, but also get the compiler to check that the same code would compile if it used a different type instead:
template <typename From, typename To>
struct xfer_refs_cv
{
using type = To;
};
template <typename From, typename To>
struct xfer_refs_cv<const From, To>
{
using type = const typename xfer_refs_cv<From, To>::type;
};
template <typename From, typename To>
struct xfer_refs_cv<volatile From, To>
{
using type = volatile typename xfer_refs_cv<From, To>::type;
};
template <typename From, typename To>
struct xfer_refs_cv<From&, To>
{
using type = typename xfer_refs_cv<From, To>::type&;
};
template <typename From, typename To>
struct xfer_refs_cv<From&&, To>
{
using type = typename xfer_refs_cv<From, To>::type&&;
};
template <typename CheckType, typename Func, typename CallType>
constexpr decltype(auto) check_and_call(Func&& f, CallType&& call_arg)
noexcept(noexcept(std::forward<Func>(f)(std::forward<CallType>(call_arg))))
{
(void) decltype(std::declval<Func&&>()
(std::declval<typename xfer_refs_cv<CallType&&, CheckType>::type>()), 0){};
return std::forward<Func>(f)(std::forward<CallType>(call_arg));
}
template<typename T>
void foo(const MyArray<T>& x)
{
check_and_call<MyArray<const T>>(
[](auto&& x) {
// Function implementation here.
}, x);
}
I was reading the std::enable_shared_from_this(g++ version) source code in the past two days. There is two questions confuse me.
Let me show brief source code first.
template<typename _Tp>
class enable_shared_from_this {
...
private:
template<typename _Tp1>
friend void
__enable_shared_from_this_helper(const __shared_count<>& __pn,
const enable_shared_from_this* __pe,
const _Tp1* __px) noexcept {
}
};
template<typename _Tp1, typename _Tp2>
void
__enable_shared_from_this_helper(const __shared_count<>&,
const enable_shared_from_this<_Tp1>*,
const _Tp2*) noexcept;
Questions:
1. note the line const enable_shared_from_this* __pe, there is no tag '<>' after enable_shared_from_this, does this imply enable_shared_from_this<__Tp1> here?
2. There is two overload function template __enable_shared_from_this_helper here, my test shows that the version defined in class enable_shared_from_this will always be called, why?
Thank you guys, would appreciate any effort.
For 1.: No, it means that the <_Tp> is implicit here, not the <_Tp1> from the additional template parameter. When you leave out the template parameters of the class name within the definition of a class (or the body of a member-method's definition), and a type is expected, the parameters from the class itself are implied.
For 2.: This is the same as the first one just with _Tp1 instead of _Tp and with _Tp2 instead of _Tp1.
I'm working on an container class template (for int,bool,strings etc), and I've been stuck with this error
cont.h:56: error: expected initializer before '&' token
for this section
template <typename T>
const Container & Container<T>::operator=(const Container<T> & rightCont){
what exactly have I done wrong there?.
Also not sure what this warning message means.
cont.h:13: warning: friend declaration `bool operator==(const Container<T>&, const Container<T>&)' declares a non-template function
cont.h:13: warning: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) -Wno-non-template-friend disables this warning
at this position
template <typename T>
class Container{
friend bool operator==(const Container<T> &rhs,const Container<T> &lhs);
public:
In the first case, you're saying just Container the first time instead of Container<T>.
In the second case, I think you need to repeat the template: while member functions of a template class are implicitly templated, friend functions aren't necessarily, so it's necessary to be more explicitly . I'm not sure what exact syntax you need for that purpose, but I think it is:
before the class, template<typename T> bool operator==( etc
in the class, bool operator==<>( etc
I think that's what the error message is trying to convey, though not super-clearly.
In the first case you've done things backwards. When you specify the return type, you have to include the template parameter list into the template identifier (Container<T>), but when you specify parameter type, you don't need to do it (just Container is enough)
template <typename T>
const Container<T> & Container<T>::operator=(const Container & rightCont){
...
You did it the other way around for some reason.
In the second case, when you declare operator == as a friend it simply warns you that that in this case operator == you are referring to is an ordinary function. It can't be a specialization of a template. I.e. for the class Container<int> the function
bool operator==(const Container<int> &rhs, const Container<int> &lhs) {
// ...
}
will be a friend. But specialization of function template
template <class U>
bool operator==(const Container<U> &rhs, const Container<U> &lhs) {
// ...
}
for U == int will not be a friend of Container<int>. If that's your intent, you are OK.
If you wanted to befriend a specific specialization of the above template, you'd have to say
template <typename T>
class Container {
friend bool operator==<T>(const Container<T> &rhs, const Container<T> &lhs);
...
If you wanted to befriend all specialization of the above template, you'd have to say
template <typename T>
class Container {
template <class U>
friend bool operator==(const Container<U> &rhs, const Container<U> &lhs);
...
I have a subset of a pointer class that look like:
template <typename T>
struct Pointer
{
Pointer();
Pointer(T *const x);
Pointer(const Pointer &x);
template <typename t>
Pointer(const Pointer<t> &x);
operator T *() const;
};
The goal of the last constructor is to allow to pass a Pointer of a subclass, or basically any type that is implicitly convertable to T *. This actual rule is only enforced by the definition of the constructor and the compiler can't actually figure it out by the declaration alone. If I drop it, and try to pass a Pointer<Sub> to a constructor of Pointer<Base>, I get a compile error, despite of the possible path through operator T *().
While it solves the above problem, it creates another one. If I have an overloaded function whose one overload takes a Pointer<UnrelatedClass> and the other takes Pointer<BaseClass>, and I try to invoke it with a Pointer<SubClass>, I get an ambiguity between the two overloads, with the intention, ofcourse, that the latter overload will be called.
Any suggestions? (Hopefully I was clear enough)
The cure for your problem is called SFINAE (substitution failure is not an error)
#include "boost/type_traits/is_convertible.hpp"
#include "boost/utility/enable_if.hpp"
template<typename T>
class Pointer {
...
template<typename U>
Pointer(const Pointer<U> &x,
typename boost::enable_if<
boost::is_convertible<U*,T*>
>::type* =0)
: ...
{
...
}
...
};
If U* is convertible to T* the enable_if will have a typedef member type defaulting to void. Then, everything is fine. If U* is not convertible to T* this typedef member is missing, substitution fails and the constructor template is ignored.
This solves your conversion and ambiguity problems.
In response to the comment: is_convertible looks something like this:
typedef char one; // sizeof == 1 per definition
struct two {char c[2];}; // sizeof != 1
template<typename T, typename U>
class is_convertible {
static T source();
static one sink(U);
static two sink(...);
public:
static const bool value = sizeof(sink(source()))==1;
};
Try to make the constructor in question explicit, e.g.:
template <typename t>
explicit Pointer(const Pointer<t> &x);
And/or remove the operator T *() const; - I think this one will also create an ambiguity.
EDIT
Check the std::auto_ptr interface, and compare with yours. At least they solved the ambiguity.