SFINAE constexpr error in Microsoft Visual C++ - c++

Simple SFINAE code using constexpr fails to compile. The same code compiles in g++.
This issue comes only when constexpr is used. While using std::is_integral_v<T>, it compiles.
// test code
#include<iostream>
template<typename T>
constexpr inline bool is_integral() {
return std::is_integral_v<T>;
}
template<typename T>
constexpr inline bool is_floating_point() {
return std::is_floating_point_v<T>;
}
struct tester {
template<typename T>
std::enable_if_t<is_integral<T>(), void> operator()(int* p){
//integral version
std::cout<<"integral\n";
}
template<typename T>
std::enable_if_t<is_floating_point<T>(), void> operator()(int* p){
//floating point version
std::cout<<"floating\n";
}
template <typename T, typename... Args>
std::enable_if_t<!is_integral<T>() && !is_floating_point<T>(), void> operator()(Args&&...)
{
std::cerr<<"not supported.\n";
}
};
enum class type { INT, FLOAT, STRING};
void type_dispatcher(type tp) {
tester t;
switch(tp) {
case type::INT:
t.operator()<int>(nullptr);
break;
case type::FLOAT:
t.operator()<float>(nullptr);
break;
default:
std::cout<<"Unsupported\n";
}
}
int main() {
type t1{type::INT}, t2{type::FLOAT}, t3{type::STRING};
type_dispatcher(t1);
type_dispatcher(t2);
type_dispatcher(t3);
return 0;
}
Error: cl.exe /std:c++17 ..\sfinae_test.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30136 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
sfinae_test.cpp
..\sfinae_test.cpp(21): error C2535: 'enable_if<0,void>::type tester::operator ()(int *)': member function already defined or declared
..\sfinae_test.cpp(16): note: see declaration of 'tester::operator ()'
..\sfinae_test.cpp(44): error C2672: 'tester::operator ()': no matching overloaded function found
..\sfinae_test.cpp(44): error C2893: Failed to specialize function template 'enable_if<0,void>::type tester::operator ()(int *)'
..\sfinae_test.cpp(16): note: see declaration of 'tester::operator ()'
..\sfinae_test.cpp(44): note: With the following template arguments:
..\sfinae_test.cpp(44): note: 'T=float'
Folding all the code into if constexpr works.
Related issue: https://developercommunity.visualstudio.com/t/constexpr-function-in-sfinae/1048586
Is there any solution/hack to this problem?

You can use enable_if as template parameter, like this.

Related

Problem in GCC/C++17 with template template class

Consider the 2 following overloads
template<typename T>
bool test() {
return true;
}
template<template<typename ...> class T>
bool test() {
return false;
}
The 1st one works for regular classes, while the 2nd one works for templates that are not instantiated.
For instance:
std::cout<<test<int>()<<std::endl; <-- this yields 1
std::cout<<test<std::list>()<<std::endl; <--this yields 0
Now consider the following template function:
template<typename U>
bool templfun(){
struct A{
bool f(){
return test<A>(); // <-- this gives an error
}
};
return test<A>(); // <-- this is ok
}
In GCC it gives an error for ambiguous overload resolution, while Clang compiles.
Interestingly, the second call to test() doesn't produce errors (even in GCC).
Moreover, if I remove the template<typename U> thing on top of templfun, gcc stops complaining.
Is this a bug with GCC or is it illegal code?
GCC is wrong; struct A is a templated entity but clearly not a template (as it does not start with a template keyword), so there is no ambiguity.
To confirm, we can rename the type parameter to see that G++ is attempting to use the template-template overload.
template <typename X>
bool test() {
return true;
}
template <template <typename...> class Y>
bool test() {
return false;
}
template <typename U>
bool templfun() {
struct A {
bool f() {
return test<A>(); // <-- this gives an error
}
};
return test<A>(); // <-- this is ok
}
bool run() {
return templfun<int>();
}
G++ output: (link to godbolt)
<source>:15:27: error: call of overloaded 'test<templfun() [with U = int]::A>()' is ambiguous
15 | return test<A>(); // <-- this gives an error
| ~~~~~~~^~
<source>:2:6: note: candidate: 'bool test() [with X = templfun() [with U = int]::A]'
2 | bool test() {
| ^~~~
<source>:7:6: note: candidate: 'bool test() [with Y = templfun()::A]'
7 | bool test() {
| ^~~~
Clearly "candidate: 'bool test() [with Y = templfun()::A]'" is bogus.
Note that local types were not allowed as template arguments prior to C++11 (see C++03 ยง 14.3.1.2), so that could explain the complexity of the G++ implementation.

GCC template argument deduction/substitution failed

The code below compiles on MSVC but fails on GCC (4.6.3). Why does it fail and what should I do to fix it?
#include <array>
class Foo {
public:
template<typename T, int N>
operator std::array<T, N>() const {
return std::array<T, N>();
}
};
int main(){
Foo val;
// both of the following lines fail on GCC with error:
// "no matching function call...", ultimately with a note:
// "template argument deduction/substitution failed"
auto a = val.operator std::array<int, 2>();
static_cast<std::array<int, 2>>(val);
return 0;
}
EDIT: The following code, however, does compile (on both compilers), despite passing in an int for std::array's template parameter.
template<int N, typename T>
struct Bar {
std::array<T, N> buf;
};
int main()
{
auto x = Bar<3, double>();
return 0;
}
If you read the full text of the error messages you get, the compiler is complaining because the type for N in your template class is int, while the second parameter of std::array is std::size_t, which is an unsigned long on your system.
Changing your template's declaration to use std::size_t N will fix the problem.
MSVC is not complaining possibly because it recognizes that the value "2" works for either case, or because of a compiler bug.

Perfect forwarding to function pointer call

I'm trying to compile simple templetized wrapper in Visual Studio 2015
template<typename Rv, typename ...Args>
Rv call(Rv(*fp)(Args...), Args&&...args) {
return (*fp)(std::forward<Args>(args)...);
}
int iArg;
void(*fn)(int);
call(fn, iArg);`
I'm getting the following compiler error:
test.cpp(30): error C2672: 'call': no matching overloaded function found
error C2782: 'Rv call(Rv (__cdecl *)(Args...),Args &&...)': template parameter 'Args' is ambiguous
1> test.cpp(22): note: see declaration of 'call'
1> test.cpp(30): note: could be 'int'
1> test.cpp(30): note: or 'int&'
Why?
Thanks in advance
You have to split args to allow correct deduction:
template <typename Rv, typename... Args, typename... Ts>
Rv call(Rv(*fp)(Args...), Ts&&...args) {
return (*fp)(std::forward<Ts>(args)...);
}
This can be made a bit more generic to call any type of callable thing by having the template parameter be a function type instead of a raw function pointer. Working example for GCC. Should work for visual studio.
#include <iostream>
#include <type_traits>
template<typename Func, typename ...Args>
typename std::result_of<Func(Args...)>::type call(Func fp, Args&&...args) {
return fp(std::forward<Args>(args)...);
}
void foo(int i) {
std::cout << i << std::endl;
}
int main() {
int iArg = 2;
void(*fn)(int) = foo;
call(fn, iArg);
return 0;
}

Error in VS2013 when attempting to partially specialize a class template with another class template

Given the following class and function templates:
template <typename WrappedType, ParameterType ParamType, bool IsOutputParameter>
class WrappedParameter; // Definition left out for brevity
template <typename T>
struct ParameterUnwrapper
{
static T UnwrapParameter(const T& in_param)
{
return in_param;
}
};
template <typename T, ParameterType ParamType, bool IsOutputParameter>
struct ParameterUnwrapper<WrappedParameter<T, ParamType, IsOutputParameter>>
{
static T UnwrapParameter(const WrappedParameter<T, ParamType, IsOutputParameter>& in_param)
{
return in_param.GetWrapped();
}
};
template <typename T>
T UnwrapParameter(T in_param)
{
return Impl::ParameterUnwrapper<T>::UnwrapParameter(in_param);
}
template <typename T>
Impl::WrappedParameter<T, Impl::POINTER_PARAMETER, true> WrapOutputPointerParameter(T in_param)
{
return Impl::WrappedParameter<T, Impl::POINTER_PARAMETER, true>(in_param);
}
template <typename MemFunc, typename ...Args>
HRESULT ExecuteAndLog(
MemFunc in_memberFunction,
const std::string& in_methodName,
Args... args) //-> decltype((m_wrapped->*in_memberFunction)(UnwrapParameter(args)...))
{
return ExecuteFunctorAndLog(
[&]() { return (m_wrapped->*in_memberFunction)(UnwrapParameter(args)...); },
in_methodName,
args...);
}
The following call: (The ExecuteAndLog)
HRESULT STDMETHODCALLTYPE AccessorWrapper::AddRefAccessor(
HACCESSOR hAccessor,
DBREFCOUNT *pcRefCount)
{
return ExecuteAndLog(
&IAccessor::AddRefAccessor,
"AddRefAccessor",
hAccessor,
WrapOutputPointerParameter(pcRefCount));
}
Gives me errors:
error C2664: 'HRESULT (HACCESSOR,DBREFCOUNT *)' : cannot convert argument 2 from 'Impl::WrappedParameter<DBREFCOUNT *,POINTER_PARAMETER,true>' to 'DBREFCOUNT *'
No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
see reference to function template instantiation 'ExecuteAndLog<HRESULT(__stdcall IAccessor::* )(HACCESSOR,DBREFCOUNT *),HACCESSOR, Impl::WrappedParameter<DBREFCOUNT *,POINTER_PARAMETER,true>>(MemFunc,const std::string &,HACCESSOR,Impl::WrappedParameter<DBREFCOUNT *,POINTER_PARAMETER,true>)' being compiled
with
[
MemFunc=HRESULT (__stdcall IAccessor::* )(HACCESSOR,DBREFCOUNT *)
]
see reference to function template instantiation 'ExecuteAndLog<HRESULT(__stdcall IAccessor::* )(HACCESSOR,DBREFCOUNT *),HACCESSOR,Impl::WrappedParameter<DBREFCOUNT *,POINTER_PARAMETER,true>>(MemFunc,const std::string &,HACCESSOR,Impl::WrappedParameter<DBREFCOUNT *,POINTER_PARAMETER,true>)' being compiled
with
[
MemFunc=HRESULT (__stdcall IAccessor::* )(HACCESSOR,DBREFCOUNT *)
]
I think I've messed up the partial specialization of ParameterUnwrapper (or my approach is just wrong). Any advice?
More information:
Impl is a nested namespace (alongside the namespace all the provided templates except ExecuteAndLog are in)
m_wrapped is of type IAccessor* (the COM interface) in this case.
enum ParameterType
{
POINTER_PARAMETER,
ARRAY_PARAMETER
};
UPDATE:
Here's a self contained example: http://codepad.org/lwTzVImb
The error I get in VS2013 for this one is:
error C2664: 'int (int,int **,size_t *)' : cannot convert argument 2 from 'WrappedParameter<int **,ARRAY_PARAMETER,true>' to 'int **'
No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
see reference to function template instantiation 'int ExecuteAndLog<int(__thiscall A::* )(int,int **,size_t *),int,WrappedParameter<int **,ARRAY_PARAMETER,true>,size_t*>(MemFunc,const std::string &,int,WrappedParameter<int **,ARRAY_PARAMETER,true>,size_t *)' being compiled
with
[
MemFunc=int (__thiscall A::* )(int,int **,size_t *)
]
I figured it out!
The issue was the return type of UnwrapParameter. Once I changed the declaration of it to
template <typename T>
auto UnwrapParameter(T in_param) -> decltype(Impl::ParameterUnwrapper<T>::UnwrapParameter(in_param))
It compiled. Shame the compiler didn't complain about the definition of that function, rather than trusting its declared return value.
I have some other problems right now but at least I've made progress.

Code compiling on gcc, but not on msvc

I have a problem compiling a template using msvc-2010. It works perfectly using gcc 4.6.3.
I have boiled down the code to the essential (it doesn't make sense of course):
//Variant that works
template <typename T, T* Ptr>
void callFun()
{
}
//Traits class (type expands to the same type T* as above)
template <typename T>
class TraitsClass
{
public:
typedef T* type;
};
//Essentially the same as callFun2, only that the
//type of Ptr is expressed indirectly over a traits class
//The usage of this class is not possible, because of the error described below
template <typename T, typename TraitsClass<T>::type Ptr>
void callFun2()
{
}
//Provides a compile constant ptr for this example
void testFun()
{
}
int main()
{
//Works
callFun<void(), &testFun>();
//Fails
callFun2<void(), &testFun>();
//Works
callFun2<void(), 0>();
return 0;
}
The Error:
error C2975: 'Ptr' : invalid template argument for 'callFun2', expected compile-time constant expression
I find it interesting, that it only fails when the second type parameter is being used through a typedef in a Traits class.
g++ compiles this example correctly without warnings, even when using -Wall -Wextra -Werror -pedantic (Except for the unused parameters, of course)
Thank you very much.
Well, I think that the answer is that compilers are not written by gods. Programming standards in the compiler industry are extremely high, MS C++ is a good compiler, but it still contain bugs. I came across the following, that is somehow similar to what you are pointing at:
template <class item_struct>
struct THeapBasedArray
{
void Sort(int (__cdecl *compareFunction)(const item_struct *item1,
const item_struct *item2));
};
struct Item { int x; };
struct ItemPtrsArray : public THeapBasedArray<Item*>
{
static int __cdecl Compare1(const Item **pp1, const Item **pp2);
typedef Item *ItemPtr;
static int __cdecl Compare2(const ItemPtr *pp1, const ItemPtr *pp2);
};
int main()
{
ItemPtrsArray vect;
vect.Sort(ItemPtrsArray::Compare1);
vect.Sort(ItemPtrsArray::Compare2);
}
The first call to Sort fails with:
cpptest1.cxx(21) : error C2664: 'THeapBasedArray::Sort' : cannot convert parameter 1 from 'int (_cdecl *)(const Item **, const Item **)' to 'int (_cdecl *)(const item_struct *, const item_struct *)
while the second call compilers fine. For me this is a bug in a compiler. Sometimes this happens. I guess this is the answer.