I'm trying to use something like the following test case:
/* Generic implementation */
template <typename T>
struct SpecWrapper {
static void bar(T const* src) {
printf("src[0] = %le\n", src[0]);
}
};
/* Volatile partial-specialization */
template <typename T>
struct SpecWrapper<T volatile> {
static void bar(T const* src) {
printf("src[0] = %le\n", src[0]);
}
};
/* Instantiate */
void foo(double volatile const* src) {
SpecWrapper<double volatile>::bar(src);
}
However this generates the following error with g++
test.cxx: In function ‘void foo(const volatile double*)’:
test.cxx:18:38: error: invalid conversion from ‘const volatile double*’ to ‘const double*’ [-fpermissive]
LowLevel<double volatile>::bar(src);
^
test.cxx:12:16: error: initializing argument 1 of ‘static void LowLevel<volatile T>::bar(const T*) [with T = double]’ [-fpermissive]
static void bar(T const* src) {
^
Can someone explain why this problem is arising? There are a few workarounds that spring to mind, but I would like to understand why it's a problem in the first place.
Should be
/* Volatile partial-specialization */
template <typename T>
struct SpecWrapper<T volatile> {
static void bar(T volatile const* src) {
printf("src[0] = %le\n", src[0]);
}
};
since T is just double.
Related
I've been trying to implement some generic wrappers (similar to std::span). However, I can't seem to get the template deduction/ conversion working. I'm not sure why. Below is a minimum "not-compiling" example
#include <array>
#include <iostream>
template<typename T>
class Wrapper
{
public:
using ElementType = T;
using ValueType = std::remove_cv<T>;
using Pointer = ElementType*;
constexpr Wrapper(const Wrapper& lhs):
mPtr(lhs.mPtr)
{}
template<typename U, std::size_t N>
Wrapper(const std::array<U,N>& lhs):
mPtr(lhs.data())
{}
template<typename U, std::size_t N>
Wrapper(std::array<U,N>& lhs):
mPtr(lhs.data())
{}
Pointer mPtr;
};
// Deduction guidelines
template <typename Type, std::size_t Size>
Wrapper(std::array<Type, Size>&)->Wrapper<Type>;
template <typename Type, std::size_t Size>
Wrapper(const std::array<Type, Size>&)->Wrapper<const Type>;
/// The function that is supposed convert array to the Wrapper
template<typename T>
void TestFunction(const Wrapper<T>& data)
{
// do something with the data
std::cout << data.mPtr;
}
// Try and use Test Function passing in an array
void LetsTest(std::array<float, 128>& data)
{
TestFunction(data);
}
Could someone please explain what I am missing or not understanding?
The compiler complaint is
#1 with ARM gcc 10.2.1 (none) : In function 'void LetsTest(std::array<float, 128>&)': :47:22: error: no matching
function for call to 'TestFunction(std::array<float, 128>&)' 47 |
TestFunction(data);
| ^ :38:6: note: candidate: 'template<class T, class U> void TestFunction(const Wrapper&)'
38 | void TestFunction(const Wrapper& data)
| ^~~~~~~~~~~~ :38:6: note: template argument deduction/substitution failed: :47:22: note:
'std::array<float, 128>' is not derived from 'const Wrapper'
47 | TestFunction(data);
| ^
I am trying to do explicit specialization for a template function called from another template function. Following is a minimum non-working example and I am trying to implement the following idea:
CInt, CDouble and CStr are equivalent of operations that I need to perform. But, CStr constructor expects a little different format.
MyClass is equivalent of a factory, which when requested will return one of the instances of CInt, CDouble or CStr.
The motivation for this structure: Assume that GetCClass function is called from a function with ~100 lines and only one difference: the type of class. The values returned from GetCClass have same APIs.
#include <iostream>
#include <memory>
#include <string>
using namespace std;
class CStrArg {
public:
const char* a;
int size;
};
class MyClass {
public:
class CStr;
class CInt;
class CDouble;
template <typename T>
typename T::Ptr GetCClass(typename T::ArgType arg);
template <typename T>
typename T::Ptr GetCClassInternal(typename T::ArgType arg);
};
class MyClass::CInt {
public:
typedef int ArgType;
typedef shared_ptr<CInt> Ptr;
static Ptr CreatePtr(ArgType i) { return Ptr(new CInt(i)); }
private:
CInt(ArgType i) : i_(i) {}
ArgType i_;
};
class MyClass::CDouble {
public:
typedef double ArgType;
typedef shared_ptr<CDouble> Ptr;
static Ptr CreatePtr(ArgType d) { return Ptr(new CDouble(d)); }
private:
CDouble(ArgType i) : i_(i) {}
ArgType i_;
};
class MyClass::CStr {
public:
typedef CStrArg ArgType;
typedef shared_ptr<CStr> Ptr;
static Ptr CreatePtr(string s) { return Ptr(new CStr(s)); }
private:
CStr(string i) : i_(i) {}
string i_;
};
//template definition
template <typename T>
typename T::Ptr MyClass::GetCClass(typename T::ArgType arg) {
return GetCClassInternal(arg);
}
template <typename T>
typename T::Ptr MyClass::GetCClassInternal(typename T::ArgType arg) {
cout << "GetCClass for all types but one" << endl;
return T::CreatePtr(arg);
}
template <>
MyClass::CStr::Ptr MyClass::GetCClassInternal<MyClass::CStr>(CStrArg arg) {
return CStr::CreatePtr(arg.a);
}
int main() {
MyClass test;
int i = 5;
double d = 1.2;
CStrArg s;
s.a = "why me";
s.size = 6;
auto iptr = test.GetCClass(i);
auto dptr = test.GetCClass(d);
auto sptr = test.GetCClass(s);
return 0;
}
I get the following error:
experimental/amandeep/proto_test/fn_template_sp.cc:88:31: note: candidate is:
experimental/amandeep/proto_test/fn_template_sp.cc:20:19: note: template<class T> typename T::Ptr MyClass::GetCClass(typename T::ArgType)
typename T::Ptr GetCClass(typename T::ArgType arg);
^
experimental/amandeep/proto_test/fn_template_sp.cc:20:19: note: template argument deduction/substitution failed:
experimental/amandeep/proto_test/fn_template_sp.cc:88:31: note: couldn't deduce template parameter ‘T’
auto iptr = test.GetCClass(i);
^
experimental/amandeep/proto_test/fn_template_sp.cc:89:31: error: no matching function for call to ‘MyClass::GetCClass(double&)’
auto dptr = test.GetCClass(d);
^
experimental/amandeep/proto_test/fn_template_sp.cc:89:31: note: candidate is:
experimental/amandeep/proto_test/fn_template_sp.cc:20:19: note: template<class T> typename T::Ptr MyClass::GetCClass(typename T::ArgType)
typename T::Ptr GetCClass(typename T::ArgType arg);
^
experimental/amandeep/proto_test/fn_template_sp.cc:20:19: note: template argument deduction/substitution failed:
experimental/amandeep/proto_test/fn_template_sp.cc:89:31: note: couldn't deduce template parameter ‘T’
auto dptr = test.GetCClass(d);
^
experimental/amandeep/proto_test/fn_template_sp.cc:90:31: error: no matching function for call to ‘MyClass::GetCClass(CStrArg&)’
auto sptr = test.GetCClass(s);
^
experimental/amandeep/proto_test/fn_template_sp.cc:90:31: note: candidate is:
experimental/amandeep/proto_test/fn_template_sp.cc:20:19: note: template<class T> typename T::Ptr MyClass::GetCClass(typename T::ArgType)
typename T::Ptr GetCClass(typename T::ArgType arg);
^
experimental/amandeep/proto_test/fn_template_sp.cc:20:19: note: template argument deduction/substitution failed:
experimental/amandeep/proto_test/fn_template_sp.cc:90:31: note: couldn't deduce template parameter ‘T’
auto sptr = test.GetCClass(s);
I have read multiple answers, but I cannot understand why this is not working. Any help is appreciated.
EDIT:
I cannot understand, but locally in my actual code I get the following:
/home/workspace/main/util/storage/smb2_proxy/smb2_proxy.cc:239:29: error: template-id ‘CreateOp<storage::smb2_proxy::Smb2Proxy::PurgeTaskOp>’ for ‘storage::smb2_proxy::Smb2Proxy::PurgeTaskOp::Ptr storage::smb2_proxy::Smb2Proxy::CreateOp(std::shared_ptr<storage::smb2_proxy::Smb2Proxy::TaskState>,storage::smb2_proxy::Smb2Proxy::PurgeTaskOp::ArgType&,storage::smb2_proxy::Smb2Proxy::PurgeTaskOp::ResultType*,storage::smb2_proxy::Smb2Proxy::DoneCb)’ does not match any template declaration
Smb2Proxy::PurgeTaskOp::Ptr Smb2Proxy::CreateOp<Smb2Proxy::PurgeTaskOp>(
^~~~~~~~~
In file included from /home/workspace/main/util/storage/smb2_proxy/smb2_proxy.cc:5:0:
/home/workspace/main/util/storage/smb2_proxy/smb2_proxy.h:160:20: note: candidate is: template<class Op> typename Op::Ptr storage::smb2_proxy::Smb2Proxy::CreateOp(std::shared_ptr<storage::smb2_proxy::Smb2Proxy::TaskState>, const typename Op::ArgType&, typename Op::ResultType*,storage::smb2_proxy::Smb2Proxy::DoneCb)
typename Op::Ptr CreateOp(std::shared_ptr<TaskState> task_state,
CreateOp -> GetCClassInternal (both are equivalent)
The compiler is not able to take specialization of CreateOp and complains that it does not match any declaration.
PS: I had another question in which I had made a mistake while posting the code. I have deleted the question and am reposting it.
The problem is that the template parameter T of GetCClass (and GetCClassInternal) is used in non-deduced contexts, it can't be deduced.
If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.
1) The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:
You can specify the template argument explicitly. e.g.
auto iptr = test.GetCClass<MyClass::CInt>(i);
auto dptr = test.GetCClass<MyClass::CDouble>(d);
auto sptr = test.GetCClass<MyClass::CStr>(s);
LIVE
Consider the following code:
#include <iostream>
#include <type_traits>
#include <typeinfo>
struct object
{
void f0(int i) {std::cout<<i<<std::endl;}
void f1(int i) const {std::cout<<i<<std::endl;}
void f2(int i) volatile {std::cout<<i<<std::endl;}
void f3(int i) const volatile {std::cout<<i<<std::endl;}
constexpr int f4(int i) const noexcept {return i;}
static void f5(int i) {std::cout<<i<<std::endl;}
template <class T> void f(T i) {std::cout<<i<<std::endl;}
};
template <class T, class C>
void print_class_containing(T C::* ptr)
{
std::cout<<"typeid(C).name() = "<<typeid(C).name()<<std::endl;
}
int main(int argc, char* argv[])
{
std::cout<<"typeid(object).name() = "<<typeid(object).name()<<std::endl;
print_class_containing(&object::f0);
print_class_containing(&object::f1);
print_class_containing(&object::f2);
print_class_containing(&object::f3);
print_class_containing(&object::f4);
//print_class_containing(&object::f5); -> Not compiling
//print_class_containing(&object::f); -> Not compiling
return 0;
}
How to write a function, and how to call it, to print the class that is "containing" a pointer to static member function (f5), and pointer to templated member function (f)?
Currently, the compiler returns:
static_member.cpp:30:5: error: no matching function for call to 'print_class_containing'
print_class_containing(&object::f5); // -> Not compiling
^~~~~~~~~~~~~~~~~~~~~~
static_member.cpp:17:6: note: candidate template ignored: could not match 'T C::*' against 'void (*)(int)'
void print_class_containing(T C::* ptr)
^
static_member.cpp:31:5: error: no matching function for call to 'print_class_containing'
print_class_containing(&object::f); // -> Not compiling
^~~~~~~~~~~~~~~~~~~~~~
static_member.cpp:17:6: note: candidate template ignored: couldn't infer template argument 'T'
void print_class_containing(T C::* ptr)
^
2 errors generated.
I'm trying to compile the following piece of code:
template <typename T, int N> void foo( const T (&array)[N]) {}
template <typename T> static int args_fwd_(T const &t) { foo(t); return 0; }
template<class ...Us> void mycall(Us... args) {
int xs[] = { args_fwd_(args)... };
}
int main(void) {
int b[4];
mycall(b);
}
The mycall function uses variadic templates and then forwards to the args_fwd_ function to call the function foo on each argument.
This works fine for most argument types (assuming I have appropriately defined foo functions). But when I try to pass a C-style array (int b[4]) it gets turned into a pointer and then it can't find the templated foo function that requires an array (not pointer). The error from gcc 4.9.3 is as follows:
error: no matching function for call to ‘foo(int* const&)’
note: candidate is:
note: template<class T, int N> void foo(const T (&)[N])
template <typename T, int N> void foo( const T (&array)[N]) {}
note: template argument deduction/substitution failed:
note: mismatched types ‘const T [N]’ and ‘int* const’
Note the part about looking for a pointer. This is the same in clang as well so apparently this is standard compliant. Is there a way to preserve that this is a C array without it getting converted to a pointer?
Yes. Use perfect forwarding:
#include <utility>
template<class ...Us> void mycall(Us&&... args) {
int xs[] = { args_fwd_(std::forward<Us>(args))... };
}
Trying to use is_class in the following segment (stripped down from a larger one), but it doesn't seem to work. What's wrong?
#include <type_traits>
template<typename U, typename = void>
struct xxxU_Impl
{
static void xxxU_push (const U& value);
};
template<typename U> void xxxU_push (const U& value) { xxxU_Impl<U>::xxxU_push (value); }
template<typename U>
struct xxxU_Impl<U *, typename std::enable_if<std::is_class<U>::value>::type>
{
static void xxxU_push (const U *& value) { }
};
class Foo
{
public:
int mFoo;
};
int main () {
Foo * pFoo = new Foo;
xxxU_push<Foo *>(pFoo);
}
This is with gcc v4.7.2 on cygwin with gcc -std=c++11 test.cpp command line.
The output is:
test.cpp: In instantiation of 'void xxxU_push(const U&) [with U = Foo*]':
test.cpp:26:23: required from here
test.cpp:9:63: error: no matching function for call to 'xxxU_Impl<Foo*, void>::xxxU_push(Foo* const&)'
test.cpp:9:63: note: candidate is:
test.cpp:14:17: note: static void xxxU_Impl<U*, typename std::enable_if<std::is_class<_Tp>::value>::type>::xxxU_push(const U*&) [with U = Foo]
test.cpp:14:17: note: no known conversion for argument 1 from 'Foo* const' to 'const Foo*&'
**
Update
:**
Here's the modified code with annotations which IMHO show that the types are now identical. Still, I'm getting a compile error.
#include <type_traits>
template<typename U, typename = void>
struct xxxU_Impl
{
static void xxxU_push (const U & value); // U=Foo*: const Foo* & value ==
// Foo const * & value
};
template<typename U> void xxxU_push (const U & value) // U=Foo*: const Foo* & value ==
// Foo const * & value
{ xxxU_Impl<U>::xxxU_push (value); }
template<typename U>
struct xxxU_Impl<U *, typename std::enable_if<std::is_class<U>::value>::type>
{
static void xxxU_push (U const * & value) { } // U=Foo: Foo const * & value
};
class Foo
{
public:
int mFoo;
};
int main () {
Foo* pFoo = new Foo;
xxxU_push<Foo*>(pFoo);
}
What's wrong?
Thx,
D
PS A similar scheme with is_enum works w/o a hitch.
The std::is_class<> trait is working fine, and the compiler is pretty much telling you what the problem is:
test.cpp:14:17: note: no known conversion for argument 1 from Foo* const to const Foo*&
You are invoking your function template this way:
xxxU_push<Foo *>(pFoo);
Which means that U will be Foo*. Now the function signature:
template<typename U>
void xxxU_push (const U& value)
Is equivalent to this:
template<typename U>
void xxxU_push (U const& value)
And after replacing Foo* for U you get this:
void xxxU_push (Foo* const& value)
Therefore, value is a constant reference to a pointer to a Foo. Inside the function, you instantiate your class template this way:
xxxU_Impl<U>::xxxU_push (value);
Which is, when substituting Foo* for U again:
xxxU_Impl<Foo*>::xxxU_push (value);
Now your class template specialization is defined this way:
template<typename U>
struct xxxU_Impl<U *, typename std::enable_if<std::is_class<U>::value>::type>
{
static void xxxU_push (const U *& value) { }
};
If you are instantiating it with Foo* as a template argument, U will be deduced to be Foo, which is a class type. Therefore, your class template gets instantiated without failures (not sure this is what you want, but this is definitely what happens), and in particular the xxxU_push() function gets instantiated this way:
static void xxxU_push (const Foo *& value) { }
Which is equivalent to this:
static void xxxU_push (Foo const*& value) { }
Can you see the difference? On the calling site you have a constant reference to a non-constant pointer, here you have a non-constant reference to a constant pointer! These two types are different, and the compiler complains that it cannot convert the argument.
You could fix your error, for instance, by changing the signature of xxxU_push() as follows:
static void xxxU_push (U * const& value) { }
// ^^^^^^^^^^
After this change, you can see the whole thing compiling here.
UPDATE:
As a follow-up from the comments, it turns out you necessarily want the static member function xxxU_push() to accept a pointer to const:
static void xxxU_push (Foo const*& value) { }
In this case, you will have to make a decision: you cannot pass a Foo* to a function which accepts a non-constant reference to a const pointer. You can, however, drop the reference:
static void xxxU_push (Foo const* value) { }
This is a first possibility to make your program compile. The second possibility is to change the call site so that it provides a pointer to const:
int main () {
Foo * pFoo = new Foo;
Foo const* pConstFoo = pFoo;
xxxU_Impl<Foo*>::xxxU_push(pConstFoo);
// ^^^^^^^^^
}