el camino para las clases definidas por el usuario estaba destinado a trabajo es a través de las operaciones de búsqueda depende argumento. ADL permite que los programas y bibliotecas eviten saturar el espacio de nombres global con sobrecargas de operadores, pero aún permite el uso conveniente de los operadores; Es decir, sin calificación explícita del espacio de nombres, que no es posible hacer con la sintaxis del operador infijo a + b
y requeriría la sintaxis de la función normal your_namespace::operator+ (a, b)
.
ADL, sin embargo, no solo busca en todas partes cualquier posible sobrecarga del operador. ADL está restringido a mirar solo las clases 'asociadas' y los espacios de nombres. El problema con std::rel_ops
es que, como se especifica, este espacio de nombres nunca puede ser un espacio de nombres asociado de ninguna clase definida fuera de la biblioteca estándar, y por lo tanto, ADL no puede funcionar con dichos tipos definidos por el usuario.
Sin embargo, si está dispuesto a hacer trampa, puede hacer que std::rel_ops
funcione.
Los espacios de nombres asociados se definen en C++ 11 3.4.2 [basic.lookup.argdep]/2. Para nuestros propósitos, el hecho importante es que el espacio de nombres del que una clase base es miembro es un espacio de nombres asociado de la clase heredada, y por lo tanto, ADL comprobará esos espacios de nombres para las funciones apropiadas.
lo tanto, si el siguiente:
#include <utility> // rel_ops
namespace std { namespace rel_ops { struct make_rel_ops_work {}; } }
fueron (de alguna manera) a encontrar su camino en una unidad de traducción, a continuación, en implementaciones compatibles (véase la siguiente sección) usted podría entonces definir sus propios tipos de clases de este modo:
namespace N {
// inherit from make_rel_ops_work so that std::rel_ops is an associated namespace for ADL
struct S : private std::rel_ops::make_rel_ops_work {};
bool operator== (S const &lhs, S const &rhs) { return true; }
bool operator< (S const &lhs, S const &rhs) { return false; }
}
y luego ADL funcionaría para su tipo de clase y encontraría los operadores en std::rel_ops
.
#include "S.h"
#include <functional> // greater
int main()
{
N::S a, b;
a >= b; // okay
std::greater<N::s>()(a, b); // okay
}
Por supuesto añadiendo make_rel_ops_work
mismo técnico hace que el programa tenga un comportamiento indefinido debido a que C++ no permite que los programas de usuario para agregar declaraciones a std
.Como ejemplo de cómo eso realmente importa y por qué, si hace esto, puede tomarse la molestia de verificar que su implementación realmente funcione correctamente con esta adición, considere:
Más arriba muestro una declaración de make_rel_ops_work
que sigue #include <utility>
. Uno podría ingenuamente esperar que incluir esto aquí no importa y que mientras el encabezado esté incluido en algún momento antes del uso de las sobrecargas del operador, entonces ADL funcionará. La especificación, por supuesto, no ofrece esa garantía y existen implementaciones reales donde ese no es el caso.
sonido metálico con libC++, debido a libc 'uso s de los espacios de nombres en línea, será (IIUC) consideran que la declaración de make_rel_ops_work
estar en un espacio de nombres distinto del espacio de nombres que contiene los <utility>
sobrecargas de operadores menos <utility>
' ++ declaración de std::rel_ops
s es lo primero. Esto se debe a que, técnicamente, std::__1::rel_ops
y std::rel_ops
son espacios de nombres distintos incluso si std::__1
es un espacio de nombres en línea. Pero si clang ve que la declaración del espacio de nombres original para rel_ops
se encuentra en un espacio de nombre en línea __1
, tratará una declaración namespace std { namespace rel_ops {
como extensión std::__1::rel_ops
en lugar de como un nuevo espacio de nombres.
Creo que este comportamiento de extensión de espacio de nombres es una extensión de clang en lugar de ser especificado por C++, por lo que es posible que ni siquiera pueda confiar en esto en otras implementaciones. En particular, gcc no se comporta de esta manera, pero afortunadamente libstdC++ no usa espacios de nombres en línea. Si no quiere depender de esta extensión luego de sonido metálico/libC++ puede escribir:
#include <__config>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace rel_ops { struct make_rel_ops_work {}; }
_LIBCPP_END_NAMESPACE_STD
pero, obviamente, entonces usted necesita para implementaciones de otras bibliotecas que utiliza. Mi declaración más simple de make_rel_ops_work
funciona para clang3.2/libC++, gcc4.7.3/libstdC++ y VS2012.
Otro problema con 'using namespace std :: rel_ops' es que los operadores no se consideran para la búsqueda dependiente de argumentos. Esto significa que, por ejemplo, 'std :: greater' no se compilará (mientras que si se definiera un 'operador' adecuado en el mismo espacio de nombres como 'my_type', o en el espacio de nombres global). –
@MikeSeymour He agregado una solución (no portátil, pero bastante portátil en la práctica) que hace que ADL funcione con rel_ops. – bames53