As practice I tried to make a wrapper around raw pointers to have cleaner memory management when using CUDA. Basically I made a PointerBase template that has a PointerBase specialization for const pointers. This template is the base template for a HostPointer and a DevicePointer template that manage memory on CPU side and GPU side respectively (but this is out of scope of this question). I struggle with the conversion from a PointerBase<T> object into the PointerBase<const T> specialization.
(Keep in mind this is more about practicing C++ templates than production use).
Here is a simple code demonstrating the behavior.
template <typename T>
struct Pointer
{
T* ptr_;
Pointer() = default;
Pointer<T>& operator=(const Pointer<T>& other) = default;
operator Pointer<const T>() const { return Pointer<const T>(ptr_); }
T* get() const { return ptr_; }
};
template <typename T>
struct Pointer<const T>
{
const T* ptr_;
Pointer() = default;
Pointer<const T>& operator=(const Pointer<const T>& other) = default;
const T* get() const { return ptr_; }
};
template <typename T>
T dereference(Pointer<const T> data)
{
return *data.get();
}
int main()
{
int value = 7.0;
Pointer<int> ptr{&value};
//int tmp3 = dereference(ptr); // not compiling
int tmp4 = dereference(Pointer<const int>(ptr)); // compiling
return 0;
}
This code fails to compile with the following error (gcc 8.4.0 on ubuntu 20.4, with c++14 standard)
main.cpp: In function ‘int main()’:
main.cpp:41:31: error: no matching function for call to ‘dereference(Pointer<int>&)’
int tmp3 = dereference(ptr); // not compiling
^
main.cpp:32:3: note: candidate: ‘template<class T> T dereference(Pointer<const T>)’
T dereference(Pointer<const T> data)
^~~~~~~~~~~
main.cpp:32:3: note: template argument deduction/substitution failed:
main.cpp:41:31: note: types ‘const T’ and ‘int’ have incompatible cv-qualifiers
int tmp3 = dereference(ptr); // not compiling
^
Why is the conversion operator not considered for calling the dereference function ?
I feel there is a connection to the fact that the Pointer<const T> type is a specialization of the Pointer template. But I still don't understand why a conversion is not happening.
I get the same result when defining a constructor in Pointer<const T> taking a Pointer<T> as a parameter.
Any help ?
PS : Interestingly, I get the same behavior with std::shared_ptr.
#include <memory>
template <typename T>
T dereference(std::shared_ptr<const T> data)
{
return *data.get();
}
int main()
{
std::shared_ptr<int> sptr(new int[10]);
int tmp1 = dereference(sptr); // not compiling
//int tmp2 = dereference(std::shared_ptr<const int>(sptr)); //compiling
return 0;
}
main.cpp: In function ‘int main()’:
main.cpp:9:32: error: no matching function for call to ‘dereference(std::shared_ptr<int>&)’
int tmp1 = dereference(sptr); // not compiling
^
main.cpp:4:3: note: candidate: ‘template<class T> T dereference(std::shared_ptr<const _Tp>)’
T dereference(std::shared_ptr<const T> data) { return *data.get(); }
^~~~~~~~~~~
main.cpp:4:3: note: template argument deduction/substitution failed:
main.cpp:9:32: note: types ‘const _Tp’ and ‘int’ have incompatible cv-qualifiers
int tmp1 = dereference(sptr); // not compiling
^
Related
The following code compiles perfectly if:
I don't include <iostream> or
I name operator== as alp::operator==.
I suppose there is a problem with <iostream> and operator==, but I don't know what.
I compile the code with gcc 7.3.0, clang++-6.0 and goldbolt. Always the same error.
The problem is that the compiler is trying to cast the parameters of operator== to const_iterator, but why? (I suppose the compiler doesn't see my version of operator==, and looks for other versions).
#include <vector>
#include <iostream> // comment and compile
namespace alp{
template <typename It_base>
struct Iterator {
using const_iterator = Iterator<typename It_base::const_iterator>;
operator const_iterator() { return const_iterator{}; }
};
template <typename It_base>
bool operator==(const Iterator<It_base>& x, const Iterator<It_base>& y)
{ return true;}
}// namespace
struct Func{
int& operator()(int& p) const {return p;}
};
template <typename It, typename View>
struct View_iterator_base{
using return_type = decltype(View{}(*It{}));
using const_iterator =
View_iterator_base<std::vector<int>::const_iterator, Func>;
};
using view_it =
alp::Iterator<View_iterator_base<std::vector<int>::iterator, Func>>;
int main()
{
view_it p{};
view_it z{};
bool x = operator==(z, p); // only compiles if you remove <iostream>
bool y = alp::operator==(z,p); // always compile
}
Error message:
yy.cpp: In instantiation of ‘struct View_iterator_base<__gnu_cxx::__normal_iterator<const int*, std::vector<int> >, Func>’:
yy.cpp:9:73: required from ‘struct alp::Iterator<View_iterator_base<__gnu_cxx::__normal_iterator<const int*, std::vector<int> >, Func> >’
yy.cpp:44:29: required from here
yy.cpp:28:42: error: no match for call to ‘(Func) (const int&)’
using return_type = decltype(View{}(*It{}));
~~~~~~^~~~~~~
yy.cpp:22:10: note: candidate: int& Func::operator()(int&) const <near match>
int& operator()(int& p) const {return p;}
^~~~~~~~
yy.cpp:22:10: note: conversion of argument 1 would be ill-formed:
yy.cpp:28:42: error: binding reference of type ‘int&’ to ‘const int’ discards qualifiers
using return_type = decltype(View{}(*It{}));
~~~~~~^~~~~~~
I've made a more minimal test case here: https://godbolt.org/z/QQonMG .
The relevant details are:
A using type alias does not instantiate a template. So for example:
template<bool b>
struct fail_if_true {
static_assert(!b, "template parameter must be false");
};
using fail_if_used = fail_if_true<true>;
will not cause a compile time error (if fail_if_used isn't used)
ADL also inspects template parameter classes. In this case, std::vector<int>::iterator is __gnu_cxx::__normal_iterator<const int*, std::vector<int> >, Func>, which has a std::vector<int> in it's template. So, operator== will check in the global namespace (always), alp (As alp::Iterator is in alp), __gnu_cxx and std.
Your View_iterator_base::const_iterator is invalid. View_iterator_base::const_interator::result_type is defined as decltype(Func{}(*std::vector<int>::const_iterator{})). std::vector<int>::const_iterator{} will be a vectors const iterator, so *std::vector<int>::const_iterator{} is a const int&. Func::operator() takes an int&, so this means that the expression is invalid. But it won't cause a compile time error if not used, for the reasons stated above. This means that your conversion operator is to an invalid type.
Since you don't define it as explicit, the conversion operator (To an invalid type) will be used to try and match it to the function parameters if they don't already match. Obviously this will finally instantiate the invalid type, so it will throw a compile time error.
My guess is that iostream includes string, which defines std::operator== for strings.
Here's an example without the std namespace: https://godbolt.org/z/-wlAmv
// Avoid including headers for testing without std::
template<class T> struct is_const { static constexpr const bool value = false; } template<class T> struct is_const<const T> { static constexpr const bool value = true; }
namespace with_another_equals {
struct T {};
bool operator==(const T&, const T&) {
return true;
}
}
namespace ns {
template<class T>
struct wrapper {
using invalid_wrapper = wrapper<typename T::invalid>;
operator invalid_wrapper() {}
};
template<class T>
bool operator==(const wrapper<T>&, const wrapper<T>&) {
return true;
}
}
template<class T>
struct with_invalid {
static_assert(!is_const<T>::value, "Invalid if const");
using invalid = with_invalid<const T>;
};
template<class T>
void test() {
using wrapped = ns::wrapper<with_invalid<T>>;
wrapped a;
wrapped b;
bool x = operator==(a, b);
bool y = ns::operator==(a, b);
}
template void test<int*>();
// Will compile if this line is commented out
template void test<with_another_equals::T>();
Note that just declaring operator const_iterator() should instantiate the type. But it doesn't because it is within templates. My guess is that it is optimised out (where it does compile because it's unused) before it can be checked to show that it can't compile (It doesn't even warn with -Wall -pedantic that it doesn't have a return statement in my example).
I'm trying to build up some code that wants to declare a local variable (say of type test, as shown below). Construction of that local variable should use a constructor that takes a special Tag argument if such a constructor exists, or the default constructor otherwise.
What we've been able to come up with is as follows, where we specialize to construct either a void argument or a Tag argument, but compilers don't like that:
#include <iostream>
using std::cout;
struct Tag { };
template <bool z>
struct helper {
using type = void;
};
template <>
struct helper<true> {
using type = Tag;
};
template <bool z>
static typename helper<z>::type get_arg() {
return typename helper<z>::type();
}
struct test {
test(void) { cout << "test(void)\n"; }
test(Tag x) { cout << "test(Tag)\n"; }
test(const test&) = delete;
test(test&&) = delete;
};
template <typename T>
void try_construct() {
// we would be selecting from one of these by template metaprogramming
T a{typename helper<false>::type()};
T b{typename helper<true>::type()};
T c{get_arg<false>()};
T d{get_arg<true>()};
// Then do stuff with the suitably-constructed instance of T
// . . .
}
int main(void) {
try_construct<test>();
return 0;
}
Compiler output:
$ g++ -std=c++11 -c foo.cpp
foo.cpp: In instantiation of 'void try_construct() [with T = test]':
foo.cpp:38:23: required from here
foo.cpp:30:37: error: no matching function for call to 'test::test(<brace-enclosed initializer list>)'
T a{typename helper<false>::type()};
^
foo.cpp:30:37: note: candidates are:
foo.cpp:22:3: note: test::test(Tag)
test(Tag x) { cout << "test(Tag)\n"; }
^
foo.cpp:22:3: note: no known conversion for argument 1 from 'void' to 'Tag'
foo.cpp:21:3: note: test::test()
test(void) { cout << "test(void)\n"; }
^
foo.cpp:21:3: note: candidate expects 0 arguments, 1 provided
foo.cpp:33:23: error: no matching function for call to 'test::test(<brace-enclosed initializer list>)'
T c{get_arg<false>()};
^
foo.cpp:33:23: note: candidates are:
foo.cpp:22:3: note: test::test(Tag)
test(Tag x) { cout << "test(Tag)\n"; }
^
foo.cpp:22:3: note: no known conversion for argument 1 from 'helper<false>::type {aka void}' to 'Tag'
foo.cpp:21:3: note: test::test()
test(void) { cout << "test(void)\n"; }
^
foo.cpp:21:3: note: candidate expects 0 arguments, 1 provided
We know how to test on the presence of the constructor, so I've left that our of the example. If that does end up being relevant in a solution taking a different approach, feel free to go that route.
Our ultimate goal is to require one of the default constructor or the Tag constructor, and neither of the copy or move constructors.
namespace details {
template<class T>
T maybe_tag_construct(std::true_type) {
return T(Tag{});
}
template<class T>
T maybe_tag_construct(std::false_type) {
return T();
}
}
template<class T>
T maybe_tag_construct() {
return details::maybe_tag_construct<T>( std::is_constructible<T, Tag>{} );
}
now auto t =maybe_tag_construct<test>(); constructs test from Tag iff it works.
It also does elided move construction before c++17, and in c++17 no move construction occurs.
In order to pass an instance of void around, you need the "regular void" proposal, which is on track for c++2a last I checked.
I think something along these lines works:
#include <type_traits>
template <typename T, bool B = std::is_constructible<Tag, T>> struct H;
template <typename T>
struct H<T, false> {
T t;
H() : t() {}
};
template <typename T>
struct H<T, true> {
T t;
H() : t(Tag) {}
};
try_construct() {
H<T> h;
h.t;
}
Boost's <boost/any.hpp> has:
template<typename ValueType>
ValueType any_cast(any & operand);
template<typename ValueType>
inline ValueType any_cast(const any & operand);
(among other variants.) Shouldn't this combination cause ambiguity in calls such as boost::any_cast<int>(my_any); ?
I'm asking because if I write this program:
#include <boost/any.hpp>
#include <iostream>
template<typename ValueType>
ValueType any_cast(boost::any & operand)
{
return boost::any_cast<ValueType>(operand);
}
int main()
{
int x = 123;
boost::any my_any(x);
std::cout << "my_any = " << any_cast<int>(my_any) << "\n";
return 0;
}
I do get a complaint about ambiguity:
g++ -std=c++14 -O3 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In function 'int main()':
main.cpp:14:57: error: call of overloaded 'any_cast(boost::any&)' is ambiguous
std::cout << "my_any = " << any_cast<int>(my_any) << "\n";
^
main.cpp:5:11: note: candidate: ValueType any_cast(boost::any&) [with ValueType = int]
ValueType any_cast(boost::any & operand)
^~~~~~~~
In file included from main.cpp:1:0:
/usr/local/include/boost/any.hpp:281:22: note: candidate: ValueType boost::any_cast(const boost::any&) [with ValueType = int]
inline ValueType any_cast(const any & operand)
^~~~~~~~
/usr/local/include/boost/any.hpp:258:15: note: candidate: ValueType boost::any_cast(boost::any&) [with ValueType = int]
ValueType any_cast(any & operand)
^~~~~~~~
Why would the calls be ambiguous? The way you call the function the any argument is an lvalue. Thus, the any argument will either be const-qualified in which case the second overload is the only potential match or it is not const-qualified in which case the first overload is the better match (there is no conversion needed while the second overload would need a conversion from any& to any const&). If you call the function with a temporary any, it could bind to an rvalue overload (i.e., taking any&&) or, if that doesn't exist, it can bind to the const-qualified overload but not the non-const-qualified overload, again, not causing any ambiguity.
Actually, there is something interesting happening here: without the overload in the global namespace the function using the explicit template argument cannot be used! However, as soon as any function template is present, even a non-matching one, it can be used! Here is an example:
namespace foo {
struct bar {};
template <typename T> void bar_cast(bar&) {}
template <typename T> void bar_cast(bar const&) {}
template <typename T> void bar_cast(bar&&) {}
}
struct whatever;
template <typename T> void bar_cast(whatever);
int main()
{
foo::bar b;
bar_cast<int>(b);
}
The following code compiles fine with g++ and fails with clang (all versions I've tested):
#include <iostream>
namespace has_insertion_operator_impl
{
typedef char no;
typedef char yes[2];
struct any_t
{
template <typename T>
any_t(const T&);
};
yes& testStreamable(std::ostream&);
no testStreamable(no);
no operator<<(const std::ostream&, const any_t&);
template <typename T>
struct has_insertion_operator
{
static std::ostream& s;
static const T& t;
static const bool value = sizeof(testStreamable(s << t)) == sizeof(yes);
};
} // namespace has_insertion_operator_impl
template <typename T>
struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator<T>
{};
enum A : bool {
Yup = true,
Nop = false,
};
template <typename T>
bool getTraitVal(const T&) { return has_insertion_operator<T>::value; }
int main() { std::cout << getTraitVal(A::Yup) << std::endl; }
The error (with clang only!) is this:
prog.cc:24:59: error: use of overloaded operator '<<' is ambiguous (with operand types 'std::ostream' (aka 'basic_ostream<char>') and 'const A')
static const bool value = sizeof(testStreamable(s << t)) == sizeof(yes);
I believe this is a small enough example. Here are links to online compilers for it:
clang 3.8
g++ 6.1
When I change the enum type from bool to int - the error disappears.
So why is this happening? This was originally discovered when using the doctest and Catch testing frameworks - here is the bug report for Catch. Could it be a clang bug?
I know it doesn't answer your question, but it seems clang has a problem with enums of underlying type 'bool'.
I further reduced your example to:
#include <iostream>
enum A : bool {
Yup = true,
Nop = false,
};
int main() {
A t = Yup;
std::cout << t;
}
And here you can already have a feeling for what's happening:
prog.cc:10:15: error: use of overloaded operator '<<' is ambiguous (with operand types 'ostream' (aka 'basic_ostream<char>') and 'A')
std::cout << t;
~~~~~~~~~ ^ ~
/usr/local/libcxx-3.8/include/c++/v1/ostream:195:20: note: candidate function
basic_ostream& operator<<(bool __n);
^
/usr/local/libcxx-3.8/include/c++/v1/ostream:198:20: note: candidate function
basic_ostream& operator<<(int __n);
^
...
I'm trying to write a member function that can instantiate an object of a custom type (templatized), initializing its const& member to a local object of the function.
This is consistent since the lifetime of the custom type object is the same as the local_object.
The objective is caching some metadata of the local object because they don't change during its lifetime. The operator() (or any member function) computes some values, then used later in func, and the objective is offering a hook to change the behaviour of func.
Please no polymorphic solutions (currently used) due to (profiled) slowness.
This is a M(N)WE:
#include <vector>
class cls {
public:
template <typename Custom> int func() {
std::vector<int> local_object{0, 14, 32};
Custom c(local_object, 42);
return c();
}
};
template<typename AType> class One {
public:
One(const AType& obj, const int n): objref(obj), param(n), member_that_should_depend_on_objref(obj.size()) {}
int operator()() { return 42; }
private:
const AType& objref;
const int param;
float member_that_should_depend_on_objref;
};
template<typename AType> class Two {
public:
Two(const AType& obj, const int n): objref(obj), param(n), other_member_that_should_depend_on_objref(obj.empty()), other_dependent_member(obj.back()) {}
int operator()() { return 24; }
private:
const AType& objref;
const int param;
bool other_member_that_should_depend_on_objref;
int other_dependent_member;
};
int main() {
cls myobj;
auto a = myobj.func<One>();
auto b = (myobj.func<Two>)();
}
G++ 5.3.0 says
tmp.cpp: In function 'int main()':
tmp.cpp:34:30: error: no matching function for call to 'cls::func()'
auto a = myobj.func<One>();
^
tmp.cpp:4:36: note: candidate: template<class Custom> int cls::func()
template <typename Custom> int func() {
^
tmp.cpp:4:36: note: template argument deduction/substitution failed:
tmp.cpp:35:32: error: no matching function for call to 'cls::func()'
auto b = (myobj.func<Two>)();
^
tmp.cpp:4:36: note: candidate: template<class Custom> int cls::func()
template <typename Custom> int func() {
^
tmp.cpp:4:36: note: template argument deduction/substitution failed:
Clang++ 3.7.1 says:
tmp.cpp:34:20: error: no matching member function for call to 'func'
auto a = myobj.func<One>();
~~~~~~^~~~~~~~~
tmp.cpp:4:36: note: candidate template ignored: invalid explicitly-specified argument for template
parameter 'Custom'
template <typename Custom> int func() {
^
tmp.cpp:35:21: error: no matching member function for call to 'func'
auto b = (myobj.func<Two>)();
~~~~~~~^~~~~~~~~~
tmp.cpp:4:36: note: candidate template ignored: invalid explicitly-specified argument for template
parameter 'Custom'
template <typename Custom> int func() {
^
2 errors generated.
auto a = myobj.func<One>();
is wrong since One is a class template, not a class. Use
auto a = myobj.func<One<SomeType>>();
It's not clear from your code what SomeType should be.
Update
If you want to use:
auto a = myobj.func<One>();
you need to change func to use a template template parameter:
class cls {
public:
template <template <class> class Custom > int func() {
std::vector<int> local_object{0, 14, 32};
Custom<std::vector<int>> c(local_object, 42);
return c();
}
};
Perhaps that was your intention.