tags:

views:

127

answers:

3

I've poked around on stackoverflow for a while, but either I don't understand templates enough to find a solution, or it simply hasn't been answered before.

In this example:

template <typename T> T f();

Is it possible to make the function require type T to be a specialization of the std::basic_string template?

I could have the template defined with T as the type of the std::basic_string as so (using std::basic_string<T> internally, of course):

template <typename T> std::basic_string<T> f();

But then I would not be able to pass std::string or std::wstring to the function (expecting the return type to be std::string and std::wstring, respectively), which is the real aim here (to be able to pass any type which derives from the std::basic_string template).

+1  A: 

This is not possible in vanilla C++. There was a language construct proposed for C++0x that would allow for this, unfortunately it was dropped.

You can do this to a certain degree with the Boost Concept Check Library, however: http://www.boost.org/doc/libs/1_44_0/libs/concept_check/concept_check.htm

Paul
I can't comment on James McNellis' post because of some inane reputation requirement so I'll say this here: I'm new to Stack Overflow, and one trend I commonly see in answering questions is to not actually answer the question, but instead ignore the problem. Is this normal for Stack Overflow, or did I simply decide to join at the wrong time?
Paul
Dunno. I'm not around here very often.I know for one that I will never use Boost. Your response does answer my question, but doesn't give me a solution.
plash
Is there a specific reason you choose not to use boost, or is it limitation from your job?Edit: Also, in the code you posted as a comment above, consider using references whenever possible and only revert to pointers when specific functionality is required.
Paul
I don't want to bloat my library. I'm not working for anyone.Also, I now know that std::basic_string is immutable, so for when strings will be passed without being modified, I'll pass them as references.
plash
@plash: `std::basic_string` is mutable.
sbi
@Paul: According to the [FAQ](http://stackoverflow.com/faq), the rep required to leave comments is at 50. While I write this, you have 61.
sbi
@sbi: Yes it is. However, when I posted I did not meet the requirements.
Paul
@Paul: Well, congratulations for taking that hurdle! `:)`
sbi
@sbi: So if I pass one as non-reference it won't be copied?
plash
@Paul: It *is* possible in vanilla C++ (no need for concepts). Have a look at Potatoswatter’s answer. Variations of his answer are routinely used in several libraries.
Konrad Rudolph
@plash: Um, I'm confused. I thought _"immutable string"_ means that its content cannot be altered? What does this have to do with copying?
sbi
@sbi: I think *I'm* the confused one. In languages that I've used that have immutable strings, copies of the object are _always_ made when you pass them by-value. Well, whatever, passing them by-ref is the right way to do it regardless of immutability.
plash
@plash: In C++, immutability has nothing to do with pass-per-value vs. pass-per-reference. `std::basic_string` is mutable, you can change a string's content. Independently of that, you are free to pass function parameters any way you like, although [there are established patterns for passing objects to functions](http://stackoverflow.com/questions/2139224/how-to-pass-objects-to-functions-in-c/2139254#2139254).
sbi
@Paul: To answer the question in your first comment: a lot of times the OP may not entirely understand that there are other, usually better, and more commonly used alternatives. I might have put my non-direct-answer answer in a comment, but it wouldn't fit in a comment and it's hard to format even small code examples in a comment. Potatoswatter gives a good solution explaining how the OP can do what he asks for, but in most cases, I don't think that's what he really needs: usually, just assuming you have a type that meets certain criteria works just fine. [Also, welcome to Stack Overflow :-)]
James McNellis
+5  A: 

Instead of requiring that T be a specialization of std::basic_string, why not just assume that T is a specialization of std::basic_string and let template instantiation fail if it isn't. Just use the things from std::basic_string that you need.

For example,

template <typename T>
T get_first_three_chars(const T& str) { return str.substr(0, 3); }

Here, we assume that T has a member function substr; if it doesn't, then instantiation will fail, resulting in a compilation error.

James McNellis
Mmm. How does this look? http://pastebin.com/xiL8TGSnI presume I shouldn't be using string pointers, but.. it compiles.EDIT: Scratch that. This works perfectly for what I needed: http://pastebin.com/kptMXUPLAnything look wrong with it?
plash
Other than it being pointers rather than references that is.. For some reason I can't edit that comment anymore, so here's what I'm left with: http://pastebin.com/6yQdTWYV
plash
@plash: You can edit comments only for a grace period of 5mins.
sbi
@Potatoswatter i fixed the obvious so i could upvote it
Johannes Schaub - litb
@Johannes, @Potatoswatter: Oops. Brain compiler fail. Thank you :-)
James McNellis
Is this more similar to my suggestion than intended? If you return `T::value_type`, then lack of such a member causes SFINAE, not a compiler error. `T f() { T::value_type x; return T(); }` would illustrate the "just let it work" option.
Potatoswatter
@Potato: Yeah, it was more similar than intended. Thanks...
James McNellis
+7  A: 

Partial specialization allows you to test whether a type is a specialization of a particular template. SFINAE is a trick that can "switch off" a function template declaration. The solution combines these techniques.

template< typename T > // by default,
struct enable_if_basic_string {}; // a type is not a basic_string

template< typename CharT, typename Traits >
struct enable_if_basic_string< basic_string< CharT, Traits > > {
    typedef basic_string< CharT, Traits > type; // same as argument type
};

// enable_if_basic_string<>::type exists only if T specializes basic_string
// if not, compiler ignores function declaration per SFINAE.
template< typename T >
typename enable_if_basic_string< T >::type
F() {
    .....
}

If you mean derivation as well as specialization, you might take a look at std::tr1::is_convertible and enable_if as well.

Potatoswatter
+1 - It definitely solves the problem. Unfortunately it also requires you to decorate functions with useless parameters, though thankfully that can be hidden with default arguments.
Paul
@Paul: No, there is no parameter there. I didn't change the template or runtime arguments of his function at all.
Potatoswatter
@Potatoswatter: Yes, for this application. I was referring to using this idiom when you already have a return type in mind. Sorry I wasn't clearer.
Paul
+1 - I've just thought about it.
Tomek
Which answer is a more correct answer to this question? I'm thinking it's this one, but I won't be using it simply because I won't expect any non basic_string specialized type, and because this one is more complex and I dislike using magic.
plash
@plash: I don't think there is any reason to artificially limit a template, if it could work equally well for other unrelated types. - @paul: `boost::enable_if` lets you choose the typedeffed `type`, therefore a hidden argument is only needed for constructors, otherwise you can always use `enable_if` on the return type.
visitor
@visitor: True, but then I could just yell at whoever defied the documentation and used a non basic_string specialized type.
plash
@plash: Has anyone suggested that you just go with the original code in the question? `template <typename T> std::basic_string<T> f();` *does* allow you to pass `std::string` or `std::wstring`; just use the template arguments `char` or `wchar_t` respectively.
Potatoswatter
@Potatoswatter: Yes it does allow that, but I wanted to avoid doing it.
plash