Difference between g++ and clang++ with enable_if - c++

I want to write a function that returns an instance of a type T but behaves differently depending on how T can be constructed. Say I have structs like these
#include <type_traits>
#include <iostream>
struct A {};
struct B {};
struct C {
C(A a) {
std::cout << "C" << std::endl;
}
};
I want to create Cs by giving them an A. I have a struct like so that uses enable_if to choose one of two functions:
struct E {
template< bool condition = std::is_constructible<C, A>::value,std::enable_if_t<condition,int> = 0>
C get() {
return C{A{}};
}
template< bool condition = std::is_constructible<C, B>::value,std::enable_if_t<condition,bool> = false>
C get() {
return C{B{}};
}
};
This compiles fine with g++82 (and I think also g++9), but clang9 gives me the error
$ clang++ --std=c++17 main.cpp
main.cpp:26:12: error: no matching constructor for initialization of 'C'
return C{B{}};
^~~~~~
main.cpp:6:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'B' to 'const C' for 1st argument
struct C {
^
main.cpp:6:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'B' to 'C' for 1st argument
struct C {
^
main.cpp:7:3: note: candidate constructor not viable: no known conversion from 'B' to 'A' for 1st argument
C(A a) {
^
1 error generated.
even though the enable_if should hide that function. (I call E e; auto c = e.get();). If I don't hardcode C but instead use a template to pass in C it works in both compilers.
template<typename T>
struct F {
template< bool condition = std::is_constructible<T, A>::value,std::enable_if_t<condition,int> = 0>
T get() {
return T{A{}};
}
template< bool condition = std::is_constructible<T, B>::value,std::enable_if_t<condition,bool> = false>
T get() {
return T{B{}};
}
};
I don't understand why clang apparently typechecks the body of the function even though the function should be disabled by enable_if.

Both compiler are right,
http://eel.is/c++draft/temp.res#8.1
The validity of a template may be checked prior to any instantiation. [ Note: Knowing which names are type names allows the syntax of every template to be checked in this way. — end note ] The program is ill-formed, no diagnostic required, if:
(8.1)
- no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or
[..]
(8.4)
- a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or
return C{B{}}; is not template dependent, and wrong. clang is fine by diagnosing the issue.

Since you seem to have access to compilers supporting c++17 you could use if constexpr instead of enable_if to accomplish what you want.
#include <iostream>
#include <type_traits>
struct A {};
struct B {};
struct C {
explicit C(A a) {
std::cout << "C" << std::endl;
}
};
template<typename T>
struct False : std::false_type {};
struct E {
template<typename T = void>
C get() const {
if constexpr (std::is_constructible_v<C, A>) {
return C{A{}};
} else if constexpr (std::is_constructible_v<C, B>) {
return C{B{}};
} else {
static_assert(False<T>::value, "Error");
}
}
};
int main() {
const auto C{E{}.get()};
return 0;
}

Related

Is it allowed to return a non-literal type from a constexpr function?

struct Outer
{
explicit constexpr Outer(int ii) : n(ii) {}
explicit constexpr Outer(double dd) : n(dd) {}
explicit constexpr Outer(double*) : Outer(3.1415926) {}
explicit constexpr Outer(int*): Outer(42) {}
template <typename R>
static
constexpr
Outer meow(R* r = nullptr) { return Outer(r); }
int as_i() const { return n.i; }
double as_d() const { return n.d; }
union Inner
{
int i;
double d;
constexpr Inner(int ii) : i(ii) {}
constexpr Inner(double dd) : d(dd) {}
~Inner () {}; // non-trivial destructor
};
Inner n;
};
//#include <type_traits>
//static_assert(std::is_literal_type_v<Outer>); // failed by both GCC and clang
int main(int argc, char**)
{
auto const o = Outer::meow(&argc);
return o.as_i();
}
The above code is compiled by GCC 12.1 but rejected by clang 14.0.0
https://godbolt.org/z/o6Tvrrdsv
clang (correctly, in my opinion) complains that
<source>:12:11: error: constexpr function's return type 'Outer' is not a literal type
Outer meow(R* r = nullptr) { return Outer(r); }
^
<source>:28:11: note: 'Outer' is not literal because it has data member 'n' of non-literal type 'Outer::Inner'
Inner n;
^
<source>:36:24: error: no matching function for call to 'meow'
auto const o = Outer::meow(&argc);
^~~~~~~~~~~
<source>:12:11: note: candidate template ignored: substitution failure [with R = int]
Outer meow(R* r = nullptr) { return Outer(r); }
^
Even though o is merely const, not constexpr, shouldn't this be an error? meow is trying to do something which shouoldn't be allowed.
(If I make o into constexpr, then GCC complains that Outer has a non-trivial destructor.)
Yes, the return type of a constexpr function must be a literal type and Outer is not. If a function definition doesn't satisfy the constexpr requirements, the program would be ill-formed.
However, you have a function template here. constexpr on a function template is allowed as long as there is at least one specialization which satisfies the requirements of a constexpr function. However, if this requirement is not fulfilled, the program isn't ill-formed. It is ill-formed, no diagnostic required (IFNDR).
There is no specialization here satisfying the requirements because you always have the Outer return type and so the program is IFNDR.
Both compilers are correct. They do not need to diagnose that the program is ill-formed.

Cannot get pointer to member funtion out of decltype

I've observed I cannot do &decltype(c)::f to get a pointer to member function f using a class instance c, but I can do &C::f to get that pointer to member function, using the class type C, which I believe is the same as decltype(c).
See this minimal example:
struct C{
int f()
{
return 5;
}
} c;
template<typename T, T t, typename S, S*s> void callCf()
{
(s->*t)();
}
int main()
{
callCf<decltype(&decltype(c)::f),&decltype(c)::f,C,&c>();
}
Compiling this gives:
In function 'int main()':
15:3: error: parse error in template argument list
15:58: error: no matching function for call to 'callCf()'
15:58: note: candidate is:
8:49: note: template<class T, T t, class S, S* s> void callCf()
8:49: note: template argument deduction/substitution failed:
15:58: error: template argument 2 is invalid
Using the following works as expected:
int main()
{
callCf<decltype(&decltype(c)::f),&C::f,C,&c>();
}
Even this works:
int main()
{
using tC = decltype(c);
callCf<decltype(&decltype(c)::f),&tC::f,C,&c>();
}
This also works:
template<typename T>
struct forward_type{
typedef T type;
};
int main()
{
callCf<decltype(&decltype(c)::f),&forward_type<decltype(c)>::type::f,C,&c>();
}
My question is: why is it not possible to use decltype to obtain a pointer to member function like this &decltype(c)::f?
Edit: #Paul Sanders has shown in the comments that the minimal example works in c++17. I'm still using c++14. Does c++17 include some changes to the language that allow my minimal example to compile?
c is an lvalue, so decltype(c) will not return the C type itself, which is why decltype(c)::f does not work. decltype(c) will actually return a C& reference type instead:
If the argument is any other expression of type T, and
...
b) if the value category of expression is lvalue, then decltype yields T&;
...
You can use std::remove_reference/_t to get the C type from C&, eg:
int main()
{
callCf<
decltype(&std::remove_reference_t<decltype(c)>::f),
&std::remove_reference_t<decltype(c)>::f,
std::remove_reference_t<decltype(c)>,
&c
>();
}
Live Demo
Which can then be simplified with a using statement:
int main()
{
using tC = std::remove_reference_t<decltype(c)>;
callCf<decltype(&tC::f), &tC::f, tC, &c>();
}
Live Demo

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.

Constraints not satisfied for template template concept requiring static template method

I'm trying to implement Functor and various other category-theoretic concepts using C++ concepts, but am getting compile errors:
http://coliru.stacked-crooked.com/a/e8b6eb387229bddf
Here's my full code (I know that requiring fmap<int, int> does not verify fmap for any two types, and I plan to change it to fmap<int, std::string> or something to achieve a slightly stronger test -- or instead, possibly alter the Functor concept so that it takes in addition to F, two types T and U and verifies the existence of fmap<T, U>, but that's all after I figure out how to fix the error that I'm getting):
#include <functional>
#include <iostream>
#include <vector>
// empty Functor_Impl struct - specialize for each functor
template<template<class> class F> struct Functor_Impl {};
// std::vector Functor implementation
template<>
struct Functor_Impl<std::vector> {
template<class T, class U>
static std::vector<U> fmap(std::vector<T> x, std::function<U(T)> f) {
std::vector<U> out;
out.reserve(x.size());
for (int i = 0; i < x.size(); i++) {
out.push_back(f(x[i]));
}
return out;
}
};
// Functor concept requires Functor_Impl<F> to have fmap
template<template<class> class F>
concept bool Functor = requires(F<int> x) {
{Functor_Impl<F>::template fmap<int, int>(x)} -> F<int>;
};
// Test function using constraint.
template<template<class> class F, class T>
requires Functor<F>
F<T> mult_by_2(F<T> a) {
return Functor_Impl<F>::template fmap<T, T>(a, [](T x) {
return x * 2;
});
}
int main() {
std::vector<int> x = {1, 2, 3};
std::vector<int> x2 = mult_by_2(x);
for (int i = 0; i < x2.size(); i++) {
std::cout << x2[i] << std::endl;
}
}
And the compile error:
lol#foldingmachinebox:~/p/website-editor$ g++ foo.cpp -std=c++17 -fconcepts -o foo
foo.cpp: In function ‘int main()’:
foo.cpp:39:38: error: cannot call function ‘F<T> mult_by_2(F<T>) [with F = std::vector; T = int]’
std::vector<int> x2 = mult_by_2(x);
^
foo.cpp:31:6: note: constraints not satisfied
F<T> mult_by_2(F<T> a) {
^~~~~~~~~
foo.cpp:24:14: note: within ‘template<template<class> class F> concept const bool Functor<F> [with F = std::vector]’
concept bool Functor = requires(F<int> x) {
^~~~~~~
foo.cpp:24:14: note: with ‘std::vector<int> x’
foo.cpp:24:14: note: the required expression ‘Functor_Impl<F>::fmap<int, int>(x)’ would be ill-formed
I'm guessing that my syntax for the concept itself is wrong - that it's treating a variable as a function, or vice versa, since I'm not very familiar with the concept syntax, and in addition some of the example code on cppreference.com does not compile under GCC's implementation (e.g. concept EqualityComparable does not compile, it must be changed to concept bool EqualityComparable).
If I remove requires Functor<F> from the mult_by_2 function declaration, then the code compiles and runs.
The problem is exactly what the error message says: Functor_Impl<F>::template fmap<int, int>(x) is not a valid expression. Functor_Impl<std::vector>::fmap has two parameters, not one.

Call type's tagged constructor if available, default otherwise

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;
}