1). Is the code below valid C++ with respect to the C++14 standard?
Yes, as far as I can tell. It's sometimes a bit hard to prove, since often there's simply nothing forbidding it. However, we can look at an example in a recent draft (post-N4296), [dcl.spec.auto]/13:
template <typename T> auto g(T t) { return t; } // #1
template auto g(int); // OK, return type is int
template char g(char); // error, no matching template
template<> auto g(double); // OK, forward declaration with
// unknown return type
The same paragraph specifies:
Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type.
So the explicit specializations of a function template must use return type deduction.
I cannot find anything that forbids different return types for different specializations. Similarly, in C++98, different return types for function template specializations (of the same primary template) can be achieved by making the return type dependent on the template arguments. By using metaprogramming, you can basically achieve the same as when using return type deduction to specify unrelated return types for the different specializations.
3). Why does the code below seem to compile and work (sometimes) using C++11 compilers?
The code in the OP is ill-formed in C++11. Return type deduction for ordinary functions (non-lamdas) is a feature introduced in C++14. A program that contains this code snippet is ill-formed. However, the Standard does not mandate that implementations (compilers) must reject ill-formed programs. It merely states in [intro.compliance]/2.2:
If a program contains a violation of any diagnosable rule [...] a conforming implementation shall issue at least one diagnostic message.
and in /8
A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any well-formed program. Implementations are required to diagnose programs that use such extensions that are ill-formed according to this International Standard. Having done so, however, they can compile and execute such programs.
(So implementations can accept this program as an extension.)
g++4.8.3 issues a warning, which counts as a diagnostic message. g++4.9 issues an error, which is also a diagnostic message. Both are compliant. By specifying -Werror
, you could tell a g++4.8.3 to reject this program. (You'd have to ask the gcc developers why they've changed that from a warning to an error.)