Range for loop and namespace std

Hi all,

Please consider the following code which happens to be a gcc testsuite testcase:

struct iterator
{
int x;
explicit iterator(int v) :x(v) {}
iterator &operator ++() { ++x; return *this; }
int operator *() { return x; }
bool operator != (const iterator &o) { return x != o.x; }
};

// User defined container class
struct container
{
int min, max;
container(int a, int b) :min(a), max(b) {}
};

// Defintion of begin() and end() functions
namespace std
{
iterator begin(container &c)
{
return iterator(c.min);
}

iterator end(container &c)
{
return iterator(c.max + 1);
}
}

int main()
{
container c(1,4);
for (int it : c)
{
}

}

Compiling it with clang gives the following error:

range-for3.C:39:17: error: invalid range expression of type ‘container’; no viable ‘begin’ function available
for (int it : c)
^ ~
1 error generated.

clang version details: clang version 3.4 (trunk 186087)
Target: i386-pc-linux-gnu
Thread model: posix

My question is: Is it legal to define the begin() and end() functions in the namespace std scope?

The standard says:

if _RangeT is a class type, the unqualified-ids begin and end are looked up in the scope of class _RangeT as if by class member access lookup (3.4.5), and if either (or both) finds at least one declaration, begin- expr and end-expr are __range.begin() and __range.end(), respectively;

— otherwise, begin-expr and end-expr are begin(__range) and end(__range), respectively, where begin and end are looked up with argument-dependent lookup (3.4.2). For the purposes of this name lookup, namespace std is an associated namespace.

As far as I can make out the function defined in the file SemaStmt.cpp:

static Sema::ForRangeStatus BuildNonArrayForRange(Sema &SemaRef, Scope *S,Expr *BeginRange, Expr *EndRange,QualType RangeType,VarDecl *BeginVar,VarDecl *EndVar,SourceLocation ColonLoc,OverloadCandidateSet *CandidateSet,ExprResult *BeginExpr,ExprResult *EndExpr,Sema::BeginEndFunction *BEF);

is responsible for locating the appropriate match for begin() and end() function definitions.

Is there a bug somewhere here or am I missing something?

Any input on the same would be helpful.

Thanks,

Rahul

No. It's not legal for user code to add anything to ::std apart from
specializations of existing entities (and the most promising
std::begin has a return type that depends on a member .begin().)

I've a feeling that the mention of std:: as an associated namespace is
now useless, given that arrays are special-cased in the language
rules, any type with member begin()/end() doesn't do the ADL, and
users can't legally add a std::begin for any type that doesn't have
member begin() . I don't know if we have an open core language issues
for that. (In an older proposed version of range-base for, it would
always use the ADL version, and we needed that to search in std to
support arrays and to default to using member begin/end. The current
rules were one of the last significant changes in C++11.)

-- James

Hi all,

Please consider the following code which happens to be a gcc testsuite
testcase:

struct iterator
{
    int x;
    explicit iterator(int v) :x(v) {}
    iterator &operator ++() { ++x; return *this; }
    int operator *() { return x; }
    bool operator != (const iterator &o) { return x != o.x; }
};

// User defined container class
struct container
{
    int min, max;
    container(int a, int b) :min(a), max(b) {}
};

// Defintion of begin() and end() functions
namespace std
{
    iterator begin(container &c)
    {
        return iterator(c.min);
    }

This has undefined behavior. You can't add new declarations to namespace
std.

    iterator end(container &c)

    {
        return iterator(c.max + 1);
    }
}

int main()
{
    container c(1,4);
    for (int it : c)
    {

This test is wrong, per DR1442:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1442

    }
}

Compiling it with clang gives the following error:

range-for3.C:39:17: error: invalid range expression of type 'container';
no viable 'begin' function available
    for (int it : c)
                ^ ~
1 error generated.

clang version details: clang version 3.4 (trunk 186087)
Target: i386-pc-linux-gnu
Thread model: posix

My question is: Is it legal to define the begin() and end() functions in
the namespace std scope?

No. See 17.6.5.2.1/1:

"The behavior of a C++ program is undefined if it adds declarations or
definitions to namespace std or to a namespace within namespace std unless
otherwise specified."

(The "unless otherwise specified" cases don't apply here.)

The standard says:

if _RangeT is a class type, the unqualified-ids begin and end are looked up
in the scope of class _RangeT as if by class member access lookup (3.4.5),
and if either (or both) finds at least one declaration, begin- expr and
end-expr are __range.begin() and __range.end(), respectively;

— otherwise, begin-expr and end-expr are begin(__range) and end(__range),
respectively, where begin and end are looked up with argument-dependent
lookup (3.4.2). For the purposes of this name lookup, namespace std is an
associated namespace.

It does not say this any more :slight_smile:

Thanks Richard and James for the quick revert.
This resolution of the standard makes things clearer.

So best practice would be to define begin() and end() as member functions of the user defined container class right?

Thanks Richard and James for the quick revert.
This resolution of the standard makes things clearer.

So best practice would be to define begin() and end() as member functions
of the user defined container class right?

The best practice would be to define these functions as member functions if
that makes sense, and as free functions in the enclosing namespace (so they
can be found by ADL) otherwise.

Thanks Richard :slight_smile: