Here's a sample program:
#include <type_traits>
#include <stdio.h>
template <typename X>
struct test
{
operator int() const { puts("?"); return 0; }
template <typename T, typename = typename std::enable_if<std::is_same<X, void*>::value, T>::type>
operator T() const { puts("T"); return 0; }
};
int main()
{
test<void*> t;
char* c = (char*)t;
switch (t)
{
case 0: break;
}
return 0;
}
And this is the error that g++-4.7 gives
user#user:~$ g++-4.7 -std=c++0x test.cpp
test.cpp: In function ‘int main()’:
test.cpp:13:14: error: ambiguous default typeconversion from ‘test<void*>’
test.cpp:13:14: error: candidate conversions include ‘template<class T, class> test::operator void*() const [with T = T; <template-parameter-2-2> = <template-parameter-1-2>; X = void*]’
g++ 4.6 compiles it without errors and different operators are actually called.
Is there a way to make this work under g++ 4.7?
UPDATE: actually it works in 4.6 without any enable_if at all... so the question still applies but I'm now not sure if enable_if will help.
If you add an explicit cast to int here:
switch ((int)t)
Then it should compile.
I think it's complaining about the conversion being ambiguous since there exists more than one type that can hold a 0 value.
I'm using g++ 4.8 though.
I found at least one "acceptable" solution:
#define switch(x) \
switch( (typename switch_type<__typeof(x)>::type)(x) )
which switch_type trait can be extended to resolve ambiguity for specific app-related types (property types).
Related
I have a C++ class template for representing real- and complex valued 2D fields. I'd like to overload the assignment operator to achieve deep-copying the data from one field to another. For now, I've restricted the data to either double or std::complex<double>. This means there are 4 different cases to consider: double-to-double, double-to-std::complex<double>, std::complex<double>-to-double and std::complex<double>-to-std::complex<double>. I want to handle the std::complex<double>-to-double case by taking the real part of the complex value; for the other cases it's just a trivial assignment. I'm however struggling to get the code to compile. The following is a simple mock version that captures the issues:
#include <complex>
template<class T>
class Number {
private:
// should make Number<T>.m_value visible to Number<U>
template<class U>
friend class Number;
T m_value;
public:
Number(const T value) : m_value{value} {
// restricting the data
static_assert(
std::is_same<T, double>::value || std::is_same<T, std::complex<double>>::value,
"Error: Number::Number: Only 'double' and 'std::complex<double>' are supported currently!"
);
// no copying allowed
Number(const Number& orig) = delete;
};
// general case
template<class U>
Number<T>& operator=(const Number<U>& another) {
m_value = another.m_value;
return *this;
}
};
// specialization
template<> Number<double>& Number<double>::operator=(const Number<std::complex<double>>& another) {
m_value = std::real(another.m_value);
}
int main(int argc, char** argv) {
const std::complex<double> I{0.0, 1.0};
Number<double> n{0.0};
Number<std::complex<double>> m{1.0 + I*2.0};
n = m;
return 0;
}
Compiler output:
g++ -c -g -std=c++14 -MMD -MP -MF "build/Debug/GNU-Linux/main.o.d" -o build/Debug/GNU-Linux/main.o main.cpp
main.cpp:28:28: error: template-id ‘operator=<>’ for ‘Number<double>& Number<double>::operator=(const Number<std::complex<double> >&)’ does not match any template declaration
template<> Number<double>& Number<double>::operator=(const Number<std::complex<double>>& another) {
^
main.cpp:28:97: note: saw 1 ‘template<>’, need 2 for specializing a member function template
template<> Number<double>& Number<double>::operator=(const Number<std::complex<double>>& another) {
^
main.cpp: In instantiation of ‘Number<T>& Number<T>::operator=(const Number<U>&) [with U = std::complex<double>; T = double]’:
main.cpp:36:5: required from here
main.cpp:23:15: error: cannot convert ‘const std::complex<double>’ to ‘double’ in assignment
m_value = another.m_value;
^
I can't seem to figure out how to implement the assignment overloads. I have tried to find a solution for my problem (e.g. from "Similar questions") and have come across many helpful questions and answers, and have incorporated many things from them to my code. I however haven't found a solution to my specific problem and seem to be stuck. Any suggestions? Thanks!
It actually says what it expects in the line:
main.cpp:28:97: note: saw 1 ‘template<>’, need 2 for specializing a member function template
You need one 'template<>' to specialize T in class template and one more to specialize U in member function template:
template<>
template<>
Number<double>& Number<double>::operator=(const Number<std::complex<double>>& another) { ... }
I may be asking a wrong question here, but what exactly am I doing wrong that it causes the compiler to think that the constraint I'm expecting on pop method of the stack is std::same_as<void, T>?
#include <concepts>
#include <stack>
template <typename S, typename T>
concept generic_stack = requires(S s, T t) {
s.push(t);
{ s.pop() } -> std::same_as<T>; // error seems to stem from here
};
template <typename T, generic_stack<T> S>
void compile_if_stack(S) {}
int main() {
compile_if_stack<int>(std::stack<int>{});
}
I tried std::same_as<decltype(s.pop()), T>; and it appears to work, but what I don't understand is what's wrong with the former approach.
Full error
# clang 12.0.1
$ clang++ -std=c++20 main.cpp
main.cpp:14:5: error: no matching function for call to 'compile_if_stack'
compile_if_stack<int>(std::stack<int>{});
^~~~~~~~~~~~~~~~~~~~~
main.cpp:11:6: note: candidate template ignored: constraints not satisfied [with T = int, S = std::stack<int>]
void compile_if_stack(S) {}
^
main.cpp:10:23: note: because 'generic_stack<std::stack<int>, int>' evaluated to false
template <typename T, generic_stack<T> S>
^
main.cpp:7:25: note: because type constraint 'std::same_as<void, int>' was not satisfied:
{ s.pop() } -> std::same_as<T>; // error seems to stem from here
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/11.1.0/../../../../include/c++/11.1.0/concepts:63:19: note: because '__detail::__same_as<void, int>' evaluated to false
= __detail::__same_as<_Tp, _Up> && __detail::__same_as<_Up, _Tp>;
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/11.1.0/../../../../include/c++/11.1.0/concepts:57:27: note: because 'std::is_same_v<void, int>' evaluated to false
concept __same_as = std::is_same_v<_Tp, _Up>;
^
1 error generated.
C++ compiler from GCC 11.1.0 comes up with semantically identical error message.
This is because stack.pop() returns void, as per documented in std::stack::pop.
The constraint is not right, you should check for top instead:
template <typename S, typename T>
concept generic_stack = requires(S s, T t) {
s.push(t);
{ s.top() } -> std::same_as<T const&>;
};
std::stack does not return a value on pop. It has a separate function for that. Namely, top. So pop() is a void function. Hence the error.
I had written some c++ code in Visual Studio and was trying to run it on my c++ code on a linux server. When I tried to compile it with G++ however, it failed to compile with tons of errors. I looked through the error and was able to simplify the problem to this:
template<int x>
struct Struct
{
template<int y>
static void F()
{
//Struct<x>::F<0>(); // compiles
//Struct<y>::G(); // compiles
Struct<y>::F<0>(); // does not compile?
}
static void G()
{
}
};
int main ()
{
Struct<0>::F<0>();
}
On Visual Studio this code compiles just fine but on G++ or Clang++, it fails to compile. Errors on G++ 8.3.0:
test.cpp: In static member function ‘static void Struct<x>::F()’:
test.cpp:9:19: error: expected primary-expression before ‘)’ token
Struct<y>::F<0>(); // does not compile?
^
test.cpp: In instantiation of ‘static void Struct<x>::F() [with int y = 0; int x = 0]’:
test.cpp:19:18: required from here
test.cpp:9:15: error: invalid operands of types ‘<unresolved overloaded function type>’ and ‘int’ to binary ‘operator<’
Struct<y>::F<0>(); // does not compile?
Errors on Clang++:
5691311/source.cpp:9:19: error: expected expression
Struct<y>::F<0>(); // does not compile?
^
See it live: https://rextester.com/AAL19278
You can change the compiler and copy the code to see the different errors.
Is there anyway I could get around this problem so that my code will compile on G++ or Clang++?
Original Code:
template<int x, int y>
ThisType add()
{
return ThisType::Create(this->x() + x, this->y() + y);
}
ResultPos to = p.add<0, 1>();
template<int x>
struct Struct
{
template<int y>
static void F()
{
Struct<y>::F<0>(); // does not compile?
}
};
should not compile. You need to specify for the compiler that F in fact requires a template list, since F is a dependent template type. Otherwise the compiler will assume that the next < is a smaller than.
template<int x>
struct Struct
{
template<int y>
static void F()
{
Struct<y>::template F<0>();
}
};
I assume Struct<x>::F<0> works, since the compiler already knows that the current type is Struct<x>, but it cannot know that y is the same as xin this case.
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.
I like to use local classes in template classes to perform constructions like "static if". But I've faced with the problem that gcc 4.8 does not want to compile my code. However 4.7 does.
This sample:
#include <type_traits>
#include <iostream>
#include <string>
using namespace std;
struct A {
void printA() {
cout << "I am A" << endl;
}
};
struct B {
void printB() {
cout << "I am B" << endl;
}
};
template <typename T>
struct Test {
void print() {
struct IfA {
constexpr IfA(T &value) : value(value) {
}
T &value;
void print() {
value.printA();
}
};
struct IfB {
constexpr IfB(T &value) : value(value) {
}
T &value;
void print() {
value.printB();
}
};
struct Else {
constexpr Else(...) {}
void print() {
}
};
typename conditional<is_same<T, A>::value, IfA, Else>::type(value).print();
typename conditional<is_same<T, B>::value, IfB, Else>::type(value).print();
}
T value;
};
int main() {
Test<A>().print();
Test<B>().print();
}
Options:
g++ --std=c++11 main.cc -o local-sfinae
Task:
Given classes A and B with different interfaces for printing.
Write a generic class Test that can print both A and B.
Do not pollute either any namespace or class scope.
Description of the code:
This is only a clean example.
I use an approach like this, because I want to generalize the construction "static if". See, that I pass the arguments to IfA and IfB classes via their fields, not directly to the print() function.
I use such constructions a lot.
I've found that these constructions should not be in (pollute) class scope. I mean they should be placed in a method scope.
So the question.
This code can not be compiled with GCC 4.8. Because it checks ALL classes, even if they are never used. But it has not instantiate them in binary (I've commented the lines that cause errors and compiled it with gcc 4.8). Proof:
$ nm local-sfinae |c++filt |grep "::If.*print"
0000000000400724 W Test<A>::print()::IfA::print()
00000000004007fe W Test<B>::print()::IfB::print()
See, there is no Test::print()::IfB::print(). (See later: 'void Test::print()::IfB::print() [with T = A]')
The errors if I compile aforementioned code with gcc 4.8:
g++ --std=c++11 main.cc -o local-sfinae
main.cc: In instantiation of 'void Test<T>::print()::IfB::print() [with T = A]':
main.cc:36:9: required from 'void Test<T>::print() [with T = A]'
main.cc:49:21: required from here
main.cc:34:17: error: 'struct A' has no member named 'printB'
value.printB();
^
main.cc: In instantiation of 'void Test<T>::print()::IfA::print() [with T = B]':
main.cc:28:9: required from 'void Test<T>::print() [with T = B]'
main.cc:50:21: required from here
main.cc:26:17: error: 'struct B' has no member named 'printA'
value.printA();
^
Is it a GCC 4.8 bug?
Or is it GCC 4.7 bug? Maybe the code should not be compiled.
Or it is a my bug, and I should not rely on the compiler behavior/should not use such approach to implement "static if".
Additional info:
This simple code compiles on 4.7, but not on 4.8. I shortened it.
struct A {
void exist() {
}
};
template <typename T>
struct Test {
void print() {
struct LocalClass {
constexpr LocalClass(T &value) : value(value) {
}
T &value;
void print() {
value.notExist();
}
};
}
T value;
};
int main() {
Test<A>().print();
}
Errors:
main.cc: In instantiation of 'void Test<T>::print()::LocalClass::print() [with T = A]':
main.cc:16:9: required from 'void Test<T>::print() [with T = A]'
main.cc:22:21: required from here
main.cc:14:17: error: 'struct A' has no member named 'notExist'
value.notExist();
^
Have tested two GCC 4.8 versions: 2012.10 and 2013.02. Hope it is GCC 4.8 bug and it can be fixed.
LocalClass is not a template. The "not instantiated if not used" rule is only applicable to member functions of class templates.
That is, when Test::print() is instantiated, everything that is inside is brought to life, including the unused member of its local class.
There is no SFINAE in your code.
SFINAE applies during template argument deduction and argument substitution (the 'S' in SFINAE stands for substitution) but the only substitution in your program happens when substituting A for T in the template parameter list of Test, which doesn't fail.
You then call print() which instantiates Test<A>::print(), which doesn't involve any substitution, and you get an error because value.notExist(); is not valid.
SFINAE has to be used in substitution contexts, such as template argument deduction caused by a function call or when deducing template parameters with default arguments.