I need to create a template class in C++. I need to make sure that the type for the template parameter will be a class with 1 int field and 1 string field (there can be more fields, but these are mandatory).
For example, in C# I could define an interface with methods or properties, like this:
interface MyInterface {
int GetSomeInteger();
string GetSomeString();
}
and then I could use it in my template class:
class MyClass<T> where T: MyInterface {}
Is there any way to do something like this in C++?
C++20 offers you the closest solution to C#:
#include <concepts>
template <class T>
concept MyInterface = requires(T x)
{
{ x.GetSomeInteger() } -> std::same_as<int>;
};
And then:
template <MyInterface T>
struct MyClass
{
// ...
};
The most common way of doing this in current versions of C++ is a technique known as "duck-typing".
It simply involves just using T as if it implements the interface and let the compiler fail if you use the class with an incompatible type.
template<typename T>
class MyClass<T> {
int foo() {
T val;
return val.GetSomeInteger();
}
};
class Valid {
public:
int GetSomeInteger() {return 0;}
};
class Invalid {
};
int main() {
// works fine
MyClass<Valid> a;
a.foo();
// fails to compile
MyClass<Invalid> b;
b.foo();
}
Mind you, there ARE ways of enforcing this a bit more formally, but the amount of code involved is often not worth the benefit.
C++20 has concepts. Some compilers already support them. For example the following with gcc (trunk) -std=c++2a -fconcepts:
#include <string>
#include <iostream>
#include <concepts>
template<typename T>
concept HasGetIntAndString = requires(T& a) {
{ a.GetSomeInteger() } -> std::same_as<int>;
{ a.GetSomeString() } -> std::same_as<std::string>;
};
template <HasGetIntAndString T>
void bar(const T& t){
std::cout << t.GetSomeInteger() << " " << t.GetSomeString();
}
struct foo {
int GetSomeInteger() const { return 42; }
std::string GetSomeString() const { return "some"; }
};
struct foo_not {
std::string GetSomeInteger() { return "some"; }
int GetSomeString() { return 42; }
};
int main(){
bar( foo{});
bar( foo_not{});
}
results in:
<source>: In function 'int main()':
<source>:28:19: error: use of function 'void bar(const T&) [with T = foo_not]' with unsatisfied constraints
28 | bar( foo_not{});
| ^
<source>:12:6: note: declared here
12 | void bar(const T& t){
| ^~~
<source>:12:6: note: constraints not satisfied
<source>: In instantiation of 'void bar(const T&) [with T = foo_not]':
<source>:28:19: required from here
<source>:6:9: required for the satisfaction of 'HasGetIntAndString<T>' [with T = foo_not]
<source>:6:30: in requirements with 'T& a' [with T = foo_not]
<source>:7:23: note: 'a.GetSomeInteger()' does not satisfy return-type-requirement
7 | { a.GetSomeInteger() } -> std::same_as<int>;
| ~~~~~~~~~~~~~~~~^~
<source>:8:22: note: 'a.GetSomeString()' does not satisfy return-type-requirement
8 | { a.GetSomeString() } -> std::same_as<std::string>;
| ~~~~~~~~~~~~~~~^~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail
Live Demo
Before C++20 you can use SFINAE. However, often it is simpler and more appropriate to not restrict the tempalte parameter more than necessary. If the template does call T::GetSomeInteger() but the type T has no such method, the template will already fail to compile without taking any further measures. SFINAE is mainly to provide nicer error messages.
Related
when reading the document of std::span, I see there is no method to remove the first element from the std::span<T>.
Can you suggest a way to solve my issue?
The large picture of my problem(I asked in another question: How to instantiatiate a std::basic_string_view with custom class T, I got is_trivial_v<_CharT> assert error) is that I would like to have a std::basic_string_view<Token>, while the Token is not a trivial class, so I can't use std::basic_string_view, and someone suggested me to use std::span<Token> instead.
Since the basic_string_view has a method named remove_prefix which remove the first element, while I also need such kinds of function because I would like to use std::span<Token> as a parser input, so the Tokens will be matched, and consumed one by one.
Thanks.
EDIT 2023-02-04
I try to derive a class named Span from std::span, and add the remove_prefix member function, but it looks like I still have build issues:
#include <string_view>
#include <vector>
#include <span>
// derived class, add remove_prefix function to std::span
template<typename T>
class Span : public std::span<T>
{
public:
// Inheriting constructors
using std::span<T>::span;
// add a public function which is similar to std::string_view::remove_prefix
constexpr void remove_prefix(std::size_t n) {
*this = subspan(n);
}
};
struct Token
{
Token(){};
Token(const Token& other)
{
lexeme = other.lexeme;
type = other.type;
}
std::string_view lexeme;
int type;
// equal operator
bool operator==(const Token& other)const {
return (this->lexeme == other.lexeme) ;
}
};
template <typename T>
struct Viewer;
template <>
struct Viewer<Token>
{
using type = Span<Token>; // std::span or derived class
};
template <>
struct Viewer<char>
{
using type = std::string_view;
};
template <typename T> using ViewerT = typename Viewer<T>::type;
template <typename T>
class Parser
{
using v = ViewerT<T>;
};
// a simple parser demo
template <typename Base, typename T>
struct parser_base {
using v = ViewerT<T>;
constexpr auto operator[](v& output) const noexcept;
};
template<typename T>
struct char_ final : public parser_base<char_<T>, T> {
using v = ViewerT<T>;
constexpr explicit char_(const T ch) noexcept
: ch(ch)
{}
constexpr inline bool visit(v& sv) const& noexcept {
if (!sv.empty() && sv.front() == ch) {
sv.remove_prefix(1);
return true;
}
return false;
}
private:
T ch;
};
template <typename Parser, typename T>
constexpr bool parse(Span<T> &input, Parser const& parser) noexcept {
return parser.visit(input);
}
int main()
{
Token kw_class;
kw_class.lexeme = "a";
std::vector<Token> token_stream;
token_stream.push_back(kw_class);
token_stream.push_back(kw_class);
token_stream.push_back(kw_class);
Span<Token> token_stream_view{&token_stream[0], 3};
auto p = char_(kw_class);
parse(token_stream_view, p);
return 0;
}
The build error looks like below:
[ 50.0%] g++.exe -Wall -std=c++20 -fexceptions -g -c F:\code\test_crtp_twoargs\main.cpp -o obj\Debug\main.o
F:\code\test_crtp_twoargs\main.cpp: In member function 'constexpr void Span<T>::remove_prefix(std::size_t)':
F:\code\test_crtp_twoargs\main.cpp:52:17: error: there are no arguments to 'subspan' that depend on a template parameter, so a declaration of 'subspan' must be available [-fpermissive]
52 | *this = subspan(n);
| ^~~~~~~
F:\code\test_crtp_twoargs\main.cpp:52:17: note: (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)
F:\code\test_crtp_twoargs\main.cpp: In instantiation of 'constexpr void Span<T>::remove_prefix(std::size_t) [with T = Token; std::size_t = long long unsigned int]':
F:\code\test_crtp_twoargs\main.cpp:113:29: required from 'constexpr bool char_<T>::visit(v&) const & [with T = Token; v = Span<Token>]'
F:\code\test_crtp_twoargs\main.cpp:125:24: required from 'constexpr bool parse(Span<T>&, const Parser&) [with Parser = char_<Token>; T = Token]'
F:\code\test_crtp_twoargs\main.cpp:141:10: required from here
F:\code\test_crtp_twoargs\main.cpp:52:24: error: 'subspan' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
52 | *this = subspan(n);
| ~~~~~~~^~~
F:\code\test_crtp_twoargs\main.cpp:52:24: note: declarations in dependent base 'std::span<Token, 18446744073709551615>' are not found by unqualified lookup
F:\code\test_crtp_twoargs\main.cpp:52:24: note: use 'this->subspan' instead
F:\code\test_crtp_twoargs\main.cpp:52:15: error: no match for 'operator=' (operand types are 'Span<Token>' and 'std::span<Token, 18446744073709551615>')
52 | *this = subspan(n);
| ~~~~~~^~~~~~~~~~~~
F:\code\test_crtp_twoargs\main.cpp:44:7: note: candidate: 'constexpr Span<Token>& Span<Token>::operator=(const Span<Token>&)'
44 | class Span : public std::span<T>
| ^~~~
F:\code\test_crtp_twoargs\main.cpp:44:7: note: no known conversion for argument 1 from 'std::span<Token, 18446744073709551615>' to 'const Span<Token>&'
F:\code\test_crtp_twoargs\main.cpp:44:7: note: candidate: 'constexpr Span<Token>& Span<Token>::operator=(Span<Token>&&)'
F:\code\test_crtp_twoargs\main.cpp:44:7: note: no known conversion for argument 1 from 'std::span<Token, 18446744073709551615>' to 'Span<Token>&&'
Any idea on how to fix this issue?
Also, I don't know how to make a general parse function:
template <typename Parser, typename T>
constexpr bool parse(Span<T> &input, Parser const& parser) noexcept {
return parser.visit(input);
}
Currently, the first argument of the parse should be a Viewer like type?
EDIT2023-02-05
Change the function as below, the above code can build correctly. This is from Benjamin Buch's answer.
constexpr void remove_prefix(std::size_t n) {
auto& self = static_cast<std::span<T>&>(*this);
self = self.subspan(n);
}
There is still one thing remains: How to generalize the parse function to accept both input types of std::string_view and Span<Token>?
If I change the parse function to this:
template <typename Parser, typename T>
constexpr bool parse(ViewerT<T> &input, Parser const& parser) noexcept {
return parser.visit(input);
}
I got such compile error:
[ 50.0%] g++.exe -Wall -std=c++20 -fexceptions -g -c F:\code\test_crtp_twoargs\main.cpp -o obj\Debug\main.o
F:\code\test_crtp_twoargs\main.cpp: In function 'int main()':
F:\code\test_crtp_twoargs\main.cpp:143:24: error: no matching function for call to 'parse(Span<Token>&, char_<Token>&)'
143 | bool result = parse(token_stream_view, p);
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~
F:\code\test_crtp_twoargs\main.cpp:125:16: note: candidate: 'template<class Parser, class T> constexpr bool parse(ViewerT<T>&, const Parser&)'
125 | constexpr bool parse(ViewerT<T> &input, Parser const& parser) noexcept {
| ^~~~~
F:\code\test_crtp_twoargs\main.cpp:125:16: note: template argument deduction/substitution failed:
F:\code\test_crtp_twoargs\main.cpp:143:24: note: couldn't deduce template parameter 'T'
143 | bool result = parse(token_stream_view, p);
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~
Any ideas?
Thanks.
BTW: I have to explicitly instantiation of the parse function call like:
bool result = parse<decltype(p), Token>(token_stream_view, p);
to workaround this issue.
Call subspan with 1 as only (template) argument to get a new span, which doesn't contain the first element.
If you use a span with a static extend, you need a new variable because the data type changes by subspan.
#include <string_view>
#include <iostream>
#include <span>
int main() {
std::span<char const, 12> text_a("a test-span");
std::cout << std::string_view(text_a) << '\n';
std::span<char const, 10> text_b = text_a.subspan<2>();
std::cout << std::string_view(text_b) << '\n';
}
If you have a dynamic extend, you can assign the result to the original variable.
#include <string_view>
#include <iostream>
#include <span>
int main() {
std::span<char const> text("a test-span");
std::cout << std::string_view(text) << '\n';
text = text.subspan(2);
std::cout << std::string_view(text) << '\n';
}
The implementation of a modifying inplace subspan version is only possible for spans with a dynamic extend. It can be implemented as a free function.
#include <string_view>
#include <iostream>
#include <span>
template <typename T>
constexpr void remove_front(std::span<T>& self, std::size_t const n) noexcept {
self = self.subspan(n);
}
int main() {
std::span<char const> text("a test-span");
std::cout << std::string_view(text) << '\n';
remove_front(text, 2);
std::cout << std::string_view(text) << '\n';
}
You can use your own spans derived from std::span if you prefer the dot-call.
#include <string_view>
#include <iostream>
#include <span>
template <typename T>
struct my_span: std::span<T> {
using std::span<T>::span;
constexpr void remove_front(std::size_t const n) noexcept {
auto& self = static_cast<std::span<T>&>(*this);
self = self.subspan(n);
}
};
int main() {
my_span<char const> my_text("a test-span");
std::cout << std::string_view(my_text) << '\n';
my_text.remove_front(2);
std::cout << std::string_view(my_text) << '\n';
}
You can also write a wrapper class to call via dot syntax. This way you can additionally implement cascadable modification calls by always returning the a reference modifier class.
#include <string_view>
#include <iostream>
#include <span>
template <typename T>
class span_modifier {
public:
constexpr span_modifier(std::span<T>& span) noexcept: span_(span) {}
constexpr span_modifier& remove_front(std::size_t const n) noexcept {
span_ = span_.subspan(n);
return *this;
}
private:
std::span<T>& span_;
};
template <typename T>
constexpr span_modifier<T> modify(std::span<T>& span) noexcept {
return span;
}
int main() {
std::span<char const> text("a test-span");
std::cout << std::string_view(text) << '\n';
modify(text).remove_front(2).remove_front(5);
std::cout << std::string_view(text) << '\n';
}
Note I use the template function modify to create an object of the wrapper class, because the names of classes cannot be overloaded. Therefore class names should always be a bit more specific. The function modify can also be overloaded for other data types, which then return a different wrapper class. This results in a simple intuitive and consistent interface for modification wrappers.
You can write remove_prefix of your version,
template <typename T>
constexpr void remove_prefix(std::span<T>& sp, std::size_t n) {
sp = sp.subspan(n);
}
Demo
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.
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'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;
}
I kind of need help! I want to define a template method for my class to access its private fields. Here is my code:
#include <string>
#include <vector>
using namespace std;
class ex
{
public:
ex(string pegah_,int amin_):pegah(pegah_),amin(amin_){}
template<typename T>
T get_field(){
if(is_same<T,string>::value)
return pegah;
else if(is_same<T,int> ::value)
return amin;
}
private:
string pegah;
int amin;
};
int main(void)
{
string i = "salam";
int o=10;
ex y(i,o);
y.get_field<string>();
}
as you see I want to use just one function. But I keep getting this error:
test.cpp: In instantiation of ‘T ex::get_field() [with T = std::basic_string<char>]’:
test.cpp:30:21: required from here
test.cpp:15:8: error: invalid conversion from ‘int’ to ‘const char*’ [-fpermissive]
return amin;
^
In file included from /usr/include/c++/4.8/string:52:0,
from test.cpp:1:
/usr/include/c++/4.8/bits/basic_string.h:490:7: error: initializing argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ [-fpermissive]
basic_string(const _CharT* __s, const _Alloc& __a = _Alloc());
can anyone help?
Instead you could lay your code out like this:
template<typename T> T get_field();
// outside the class:
template<> inline int ex::get_field<int>() { return amin; }
template<> inline string ex::get_field<string>() { return pegah; }
As you have it now, all branches of the if..else must compile.
Basically you have three options to do it.
First using explicit specialization of template member function.
class Foo {
public:
template <typename T>
T get () const;
private:
std::string str {"XXX"};
int value {42};
};
template <>
inline std::string Foo::get () const {
return str;
}
template <>
inline int Foo::get () const {
return value;
}
Second one is to use helper function with different parameters type.
class Foo2 {
public:
template <typename T>
T get () const {
return get_helper (typename std::is_same<T, std::string>::type {});
}
private:
std::string get_helper (std::true_type) const {
return str;
}
int get_helper (std::false_type) const {
return value;
}
private:
std::string str {"YYY"};
int value {44};
};
Third option is to use SFINAE.
class Foo3 {
public:
template <typename T>
typename std::enable_if<std::is_same<T, std::string>::value, T>::type get () const {
return str;
}
template <typename T>
typename std::enable_if<std::is_same<T, int>::value, T>::type get () const {
return value;
}
private:
std::string str {"ZZZ"};
int value {45};
};
and usage would be like:
template <typename T>
void show (T v) {
std::cout << v << std::endl;
}
Foo f1;
show (f1.get<std::string> ());
show (f1.get<int> ());
Foo2 f2;
show (f2.get<std::string> ());
show (f2.get<int> ());
Foo3 f3;
show (f3.get<std::string> ());
show (f3.get<int> ());
Second option is helpful when you want to distinguish between two types. If you have more getters, then probably you will need to use first or third option.
I think it is better you define a getter and setter for each field. That is a better approach. It's easier to read and to understand and you achieve the same as with the template technique.
Explanation of your code:
It does not compile because of type checking. Template functions are generated when used in C++11. You use it with template parameter string so the function is generated. The problem is that you generate a function that returns T as a string, but you have code in your function that returns int (variable amin). Generate the function in your mind like so for T equals string:
string get_field(){
if(is_same<string,string>::value)
return pegah; // OK
else if(is_same<string,int> ::value)
return amin; // NOT OK, amin is of type int
}
One solution is that of M.M, it's called specialization. You specialize a template for (a) specific argument(s). And there are also other answers coming up.
I do not recommend that, because you finally do nothing else but generating getter functions for each variable in a specialized template. You could as well just have written:
string get_pegah(){ return pegah; }
int get_amin() { return amin; }
Easier to read, to maintain and straight forward. And more efficient I think.
as you see I want to use just one function
You don't really. You either call get_field<string> or get_field<int> and when called the appropriate function would be generated; either with T=string, T=int or both (depending on your use case). Though as you have learned by now, it's an error to do so in that case.
What you probably meant was that you want to have one function definition to do what you want. I don't think that is possible.